前言

在使用开源流程引擎(如:JBPM、Activiti、Flowable、Camunda等)的时候,经常会遇到这样的需求,我们需要按照业务需求增加一张数据库的表,而且这张表是跟工作流引擎有交互的(注意不是一张业务表),那么如何扩展一张数据库表并无缝地融入到流程引擎的机制中呢?下面以Camunda BPM为例,介绍如何扩展自定义数据库表。

模拟一个客户需求

假设某一客户的业务流程很多,有几百个,这些流程在camunda里是平层放的,没有按照业务归类,不便于管理和使用,客户希望把这些流程按照业务分类展示,就像一棵目录树,分A、B、C一层目录,A下面又分A1、A2、A3第二层目录,A1、A2、A3下面放的是具体的业务流程定义。
我们分析这个需求,需要扩展一张数据库表记录业务分类目录,同时需要在流程定义表里扩展一个字段,关联这个业务分类目录,这样才能实现流程定义按照业务分类展示。

扩展数据库表步骤

1. 建表

在camunda数据库里创建一张表,命名为act_re_proc_catalog(流程分类目录表)。


MySQL建表语句:

CREATE TABLE `act_re_proc_catalog` (
`ID_` varchar(64) COLLATE utf8_bin NOT NULL DEFAULT '' COMMENT '主键',
`CATALOG_CODE_` varchar(255) COLLATE utf8_bin DEFAULT NULL COMMENT '业务分类编码',
`CATALOG_NAME_` varchar(255) COLLATE utf8_bin DEFAULT NULL COMMENT '业务分类名称',
`PARENT_ID_` varchar(64) COLLATE utf8_bin DEFAULT NULL COMMENT '父节点ID',
`CREATE_TIME_` datetime DEFAULT NULL COMMENT '插入或修改时间',
`TENANT_ID_` varchar(64) COLLATE utf8_bin DEFAULT NULL,
PRIMARY KEY (`ID_`) USING BTREE,
KEY `ACT_IDX_PROC_CATALOG_TENANT_ID` (`TENANT_ID_`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin ROW_FORMAT=COMPACT;

2. 创建实体类

按照camunda的规则,创建实体类必须基于接口,所以先创建接口,接口命名为ProcessCatalog。

import java.util.Date;
public interface ProcessCatalog { String getId(); String getCatalogCode() ; String getCatalogName() ; String getParentId() ; Date getCreateTime() ; String getTenantId(); }

接着创建实体类,实现上面的接口和DbEntity接口,实体类命名为ProcessCatalogEntity。

import org.camunda.bpm.engine.impl.db.DbEntity;
import org.camunda.bpm.engine.repository.ProcessCatalog; import java.io.Serializable;
import java.util.Date; /** * 流程目录实体类 */
public class ProcessCatalogEntity implements Serializable, ProcessCatalog, DbEntity { private static final long serialVersionUID = 1L; protected String id;
protected String catalogCode;
protected String catalogName;
protected String parentId;
protected Date createTime;
protected String tenantId; public Object getPersistentState() { // properties of this entity are immutable
// so always the same value is returned
// so never will an update be issued for a DeploymentEntity
return ProcessCatalogEntity.class;
} public String getId() { return id;
} public void setId(String id) { this.id = id;
} public String getCatalogCode() { return catalogCode;
} public void setCatalogCode(String catalogCode) { this.catalogCode = catalogCode;
} public String getCatalogName() { return catalogName;
} public void setCatalogName(String catalogName) { this.catalogName = catalogName;
} public String getParentId() { return parentId;
} public void setParentId(String parentId) { this.parentId = parentId;
} public Date getCreateTime() { return createTime;
} public void setCreateTime(Date createTime) { this.createTime = createTime;
} public String getTenantId() { return tenantId;
} public void setTenantId(String tenantId) { this.tenantId = tenantId;
} @Override
public String toString() { return this.getClass().getSimpleName()
+ "[id=" + id
+ ", catalogCode=" + catalogCode
+ ", catalogName=" + catalogName
+ ", parentId=" + parentId
+ ", createTime=" + createTime
+ ", tenantId=" + tenantId
+ "]";
} }

3. 创建mybatis mapper文件

mapper文件命名为ProcessCatalog.xml

insert into ${prefix}ACT_RE_PROC_CATALOG(ID_, CATALOG_CODE_, CATALOG_NAME_, PARENT_ID_, CREATE_TIME_, TENANT_ID_)
values(#{id, jdbcType=VARCHAR}, #{catalogCode, jdbcType=VARCHAR}, #{catalogName, jdbcType=VARCHAR}, #{parentId, jdbcType=VARCHAR}, #{createTime, jdbcType=TIMESTAMP}, #{tenantId, jdbcType=VARCHAR}) update ${prefix}ACT_RE_PROC_CATALOG PROCDEF_ = #{procdef, jdbcType=VARCHAR},
CATALOG_CODE_ = #{catalogCode, jdbcType=VARCHAR},
CATALOG_NAME_ = #{catalogName, jdbcType=VARCHAR},
PARENT_ID_ = #{parentId, jdbcType=VARCHAR},
CREATE_TIME_ = #{createTime, jdbcType=TIMESTAMP},
TENANT_ID_ = #{tenantId, jdbcType=VARCHAR} where ID_= #{id, jdbcType=VARCHAR} delete from ${prefix}ACT_RE_PROC_CATALOG where ID_ = #{id}
select * from ${prefix}ACT_RE_PROC_CATALOG where ID_ = #{id} ${limitBefore}
select ${distinct} RES.*
${limitBetween} ${orderBy}
${limitAfter} select count(distinct RES.ID_) from ${prefix}ACT_RE_PROC_CATALOG RES RES.ID_ = #{id} and RES.CATALOG_CODE_ = #{catalogCode} and RES.CATALOG_NAME_ = #{catalogName} and RES.PARENT_ID_ = #{parentId} and RES.CREATE_TIME_ < #{createTimeBefore} and RES.CREATE_TIME_ > #{createTimeAfter} and ( RES.TENANT_ID_ in #{tenantId} or RES.TENANT_ID_ is null ) and RES.TENANT_ID_ is null
select *
from ${prefix}ACT_RE_PROC_CATALOG
where PARENT_ID_ = #{parameter.parentId} and CATALOG_CODE_ = #{parameter.catalogCode} and CATALOG_NAME_ = #{parameter.catalogName} and TENANT_ID_ is null and TENANT_ID_ = #{parameter.tenantId} order by CATALOG_CODE_ asc ${limitBefore}
select ${distinct} RES.*
${limitBetween} ${orderBy}
${limitAfter} select count(distinct RES.ID_)

4. 创建数据查询类

按照camunda的规则,创建查询类必须基于接口,所以先创建查询接口,接口命名为ProcessCatalogQuery.java。

import org.camunda.bpm.engine.impl.QueryPropertyImpl;
import org.camunda.bpm.engine.query.Query;
import org.camunda.bpm.engine.query.QueryProperty;
import java.util.Date; /** * 流程分类查询接口 */
public interface ProcessCatalogQuery extends Query<ProcessCatalogQuery, ProcessCatalog>{ public static final QueryProperty CATALOG_CODE = new QueryPropertyImpl("CATALOG_CODE_");
public static final QueryProperty CREATE_TIME = new QueryPropertyImpl("CREATE_TIME_");
public static final QueryProperty TENANT_ID = new QueryPropertyImpl("TENANT_ID_"); ProcessCatalogQuery id(String id); ProcessCatalogQuery catalogCode(String catalogCode) ; ProcessCatalogQuery catalogName(String catalogName); ProcessCatalogQuery parentId(String parentId) ; ProcessCatalogQuery createTimeBefore(Date before); ProcessCatalogQuery createTimeAfter(Date after) ; ProcessCatalogQuery tenantIdIn(String... tenantIds) ; ProcessCatalogQuery withoutTenantId() ; ProcessCatalogQuery orderByCatalogCode(); ProcessCatalogQuery orderByCreateTime(); ProcessCatalogQuery orderByTenantId(); }

然后创建查询实现类,命名为ProcessCatalogQueryImpl.java

import org.camunda.bpm.engine.impl.interceptor.CommandContext;
import org.camunda.bpm.engine.impl.interceptor.CommandExecutor;
import org.camunda.bpm.engine.impl.util.CompareUtil;
import org.camunda.bpm.engine.repository.ProcessCatalog;
import org.camunda.bpm.engine.repository.ProcessCatalogQuery; import java.io.Serializable;
import java.util.Date;
import java.util.List;
import static org.camunda.bpm.engine.impl.util.EnsureUtil.ensureNotNull; /** * 流程表单配置查询实现 */
public class ProcessCatalogQueryImpl extends AbstractQuery<ProcessCatalogQuery, ProcessCatalog> implements ProcessCatalogQuery, Serializable { private static final long serialVersionUID = 1L;
protected String id;
protected String catalogCode;
protected String catalogName;
protected String parentId;
protected Date createTime;
protected String tenantId;
protected Date createTimeBefore;
protected Date createTimeAfter;
protected boolean isTenantIdSet = false;
protected String[] tenantIds; public ProcessCatalogQueryImpl() { } public ProcessCatalogQueryImpl(CommandExecutor commandExecutor) { super(commandExecutor);
} public ProcessCatalogQuery id(String id) { ensureNotNull("Id ", id);
this.id = id;
return this;
} public ProcessCatalogQuery catalogCode(String catalogCode) { ensureNotNull("catalogCode", catalogCode);
this.catalogCode = catalogCode;
return this;
} public ProcessCatalogQuery catalogName(String catalogName) { ensureNotNull("catalogName", catalogName);
this.catalogName = catalogName;
return this;
} public ProcessCatalogQuery parentId(String parentId) { ensureNotNull("parentId", parentId);
this.parentId = parentId;
return this;
} public ProcessCatalogQuery createTimeBefore(Date before) { ensureNotNull("createTimeBefore", before);
this.createTimeBefore = before;
return this;
} public ProcessCatalogQuery createTimeAfter(Date after) { ensureNotNull("createTimeAfter", after);
this.createTimeAfter = after;
return this;
} public ProcessCatalogQuery tenantIdIn(String... tenantIds) { ensureNotNull("tenantIds", (Object[]) tenantIds);
this.tenantIds = tenantIds;
isTenantIdSet = true;
return this;
} public ProcessCatalogQuery withoutTenantId() { isTenantIdSet = true;
this.tenantIds = null;
return this;
} protected boolean hasExcludingConditions() { return super.hasExcludingConditions() || CompareUtil.areNotInAscendingOrder(createTimeAfter, createTimeBefore);
} public ProcessCatalogQuery orderByCatalogCode() { return orderBy(ProcessCatalogQuery.CATALOG_CODE);
} public ProcessCatalogQuery orderByCreateTime() { return orderBy(ProcessCatalogQuery.CREATE_TIME);
} public ProcessCatalogQuery orderByTenantId() { return orderBy(ProcessCatalogQuery.TENANT_ID);
} @Override
public long executeCount(CommandContext commandContext) { checkQueryOk();
return commandContext.getProcessCatalogManager().findProcessCatalogCountByQueryCriteria(this);
} @Override
public List executeList(CommandContext commandContext, Page page) { checkQueryOk();
return commandContext.getProcessCatalogManager().findProcessCatalogsByQueryCriteria(this, page);
} public String getId() { return id;
} public String getCatalogCode() { return catalogCode;
}
public String getCatalogName() { return catalogName;
}
public String getParentId() { return parentId;
} public Date getCreateTimeBefore() { return createTimeBefore;
} public Date getCreateTimeAfter() { return createTimeAfter;
} }

5. 创建数据库操作类

数据库操作类类似于DAO类,camunda的规则为xxxManager,我们命名该类为ProcessCatalogManager.java

import org.camunda.bpm.engine.impl.Page;
import org.camunda.bpm.engine.impl.ProcessCatalogQueryImpl;
import org.camunda.bpm.engine.impl.db.ListQueryParameterObject;
import org.camunda.bpm.engine.impl.persistence.AbstractManager;
import org.camunda.bpm.engine.repository.ProcessCatalog;
import java.util.HashMap;
import java.util.List;
import java.util.Map; /** * 流程分类数据库操作类 */
public class ProcessCatalogManager extends AbstractManager { public void insertProcessCatalog(ProcessCatalogEntity processCatalogEntity) { getDbEntityManager().insert(processCatalogEntity);
getDbEntityManager().update(ProcessCatalogEntity.class,"updateProcessCatalog",processCatalogEntity);
} public void updateProcessCatalog(ProcessCatalogEntity processCatalogEntity) { getDbEntityManager().update(ProcessCatalogEntity.class,"updateProcessCatalog",processCatalogEntity);
} public void deleteProcessCatalog(String id, final boolean cascade) { if (cascade) {
}
getDbEntityManager().delete(ProcessCatalogEntity.class, "deleteProcessCatalog", id);
} public List findProcessCatalogsByParentId(String parentId) { Map<String, Object> parameters = new HashMap<String, Object>();
parameters.put("parentId", parentId);
ListQueryParameterObject parameterObject = new ListQueryParameterObject();
parameterObject.setParameter(parameters);
List list = getDbEntityManager().selectList("selectProcessCatalogsByProcdef", parameterObject);
return list;
} public ProcessCatalogEntity findProcessCatalogById(String id) { return getDbEntityManager().selectById(ProcessCatalogEntity.class, id);
} public long findProcessCatalogCountByQueryCriteria(ProcessCatalogQueryImpl processCatalogQuery) { configureQuery(processCatalogQuery);
return (Long) getDbEntityManager().selectOne("selectProcessCatalogCountByQueryCriteria", processCatalogQuery);
} @SuppressWarnings("unchecked")
public List findProcessCatalogsByQueryCriteria(ProcessCatalogQueryImpl processCatalogQuery, Page page) { configureQuery(processCatalogQuery);
return getDbEntityManager().selectList("selectProcessCatalogsByQueryCriteria", processCatalogQuery, page);
} @Override
public void close() { } @Override
public void flush() { } protected void configureQuery(ProcessCatalogQueryImpl query) { getAuthorizationManager().configureCatalogQuery(query);
getTenantManager().configureQuery(query);
}
}

6. 在mybatis中注册mapping

我们自定义的的mapping文件需要注册到全局的mappings.xml中,其实就是mybatis的机制,找到mappings.xml文件,增加如下一条记录。

<mapper resource="org/camunda/bpm/engine/impl/mapping/entity/ProcessCatalog.xml" />

7. 在上下文中注册数据库操作类

Camunda中大量应用了命令模式,有自己的一套事务管理机制,数据库操作类必须要在CommandContext.java中进行注册。

public ProcessCatalogManager getProcessCatalogManager() {

    return getSession(ProcessCatalogManager.class);
}

8. 在全局配置中注册数据库操作类

在ProcessEngineConfigurationImpl.java类的initSessionFactories方法中注册数据库操作类ProcessCatalogManager。

addSessionFactory(new GenericManagerFactory(ProcessCatalogManager.class));

总结

通过以上的8个步骤,就可以在camunda中任意扩展数据库表了,同时我们也深入了解camunda原生数据操作机制。

开源流程引擎Camunda BPM如何扩展数据库表的更多相关文章

  1. 基于开源流程引擎开发BPM或OA有哪些难点

    前言     如何基于开源流程引擎开发OA系统?开源流程引擎哪个好?把它整合到自己的产品里难不难,有没有啥风险?这是大家经常遇到的问题.笔者从2006年开始参与流程引擎开发,经历了三代流程引擎研发,支 ...

  2. 开源流程引擎camunda如何扩展

    ​  市场上基于Java语言的开源工作流引擎有:osworkflow.jbpm.activiti.flowable.camunda等,其中osworkflow.jbpm流程引擎已经过时,目前主流的开源 ...

  3. 开源流程引擎osworkflow、jbpm、activiti、flowable、camunda哪个好?

    市场上比较有名的开源流程引擎有osworkflow.jbpm.activiti.flowable.camunda.其中:Jbpm4.Activiti.Flowable.camunda四个框架同宗同源, ...

  4. 基于camunda开源流程引擎如何实现会签及会签原理解析

    一.背景 市场上比较有名的开源流程引擎有osworkflow.jbpm.activiti.flowable.camunda.由于jbpm.activiti.flowable这几个流程引擎出现的比较早, ...

  5. 开源流程引擎该如何选择flowable还是camunda

    市场上比较有名的开源流程引擎有osworkflow.jbpm.activiti.flowable.camunda.现在国内用的最多的是activiti.flowable.camunda,下面主要从功能 ...

  6. Camunda开源流程引擎快速入门——Hello World

    市场上比较有名的开源流程引擎有osworkflow.jbpm.activiti.flowable.camunda.由于jbpm.activiti.flowable这几个流程引擎出现的比较早,国内人用的 ...

  7. Fixflow引擎解析(一)(介绍) - Fixflow开源流程引擎介绍

    Fixflow引擎解析(四)(模型) - 通过EMF扩展BPMN2.0元素 Fixflow引擎解析(三)(模型) - 创建EMF模型来读写XML文件 Fixflow引擎解析(二)(模型) - BPMN ...

  8. camunda开源流程引擎的数据库表结构介绍

    Camunda bpm流程引擎的数据库由多个表组成,表名都以ACT开头,第二部分是说明表用途的两字符标识.本文以Camunda7.11版本为例,共47张表. ACT_RE_*: 'RE'表示流程资源存 ...

  9. 基于laravel的有偿开源流程引擎

    系统主要文档已经编写完成,具体请前往查看[系统文档](https://www.kancloud.cn/lijianlin/jishullin_workflow_engine/1894424 " ...

随机推荐

  1. js知识梳理3:创建对象的模式探究

    写在前面 注:这个系列是本人对js知识的一些梳理,其中不少内容来自书籍:Javascript高级程序设计第三版和JavaScript权威指南第六版,感谢它们的作者和译者.有发现什么问题的,欢迎留言指出 ...

  2. centos下 Docker-修改磁盘存储目录(实操)

    预备知识: Docker 是一个开源项目,诞生于 2013 年初,最初是 dotCloud 公司内部的一个业余项目.它基于 Google 公司推出的 Go 语言实现. 项目后来加入了 Linux 基金 ...

  3. Py点亮

  4. 查找SQL SERVER卡顿语句

    SELECT [session_id], [blocking_session_id] AS '正在阻塞其他会话的会话ID', DB_NAME([database_id]) AS '数据库名称', [r ...

  5. go 中 select 源码阅读

    深入了解下 go 中的 select 前言 1.栗子一 2.栗子二 3.栗子三 看下源码实现 1.不存在 case 2.select 中仅存在一个 case 3.select 中存在两个 case,其 ...

  6. Android第1-2周作业

    作业1:安装环境,截图编程界面,截图运行界面 作业2:九宫格 <?xml version="1.0" encoding="utf-8"?> < ...

  7. spring4 mvc + jpa demo

    BEGIN; pom.xml 的引入的相关jar版本配置 <properties> <project.build.sourceEncoding>UTF-8</projec ...

  8. 3.2 常用Linux命令

    1.ifconfig命令 ifconfig命令用于获取网卡配置与网络状态等信息,英文全称为"interface config",语法格式为"ifconfig [参数] [ ...

  9. 论文解读(Debiased)《Debiased Contrastive Learning》

    论文信息 论文标题:Debiased Contrastive Learning论文作者:Ching-Yao Chuang, Joshua Robinson, Lin Yen-Chen, Antonio ...

  10. Vue图片浏览组件v-viewer使用

    简单介绍v-viewer的两种使用方法: Demo 安装依赖: npm install v-viewer --save 全局引入 import Viewer from 'v-viewer' impor ...