前言

在使用开源流程引擎(如: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. Python知识结构

    Python知识结构(点我) 欢迎评论提修改意见

  2. Java报错:Unable to find setter method for attribute: [x]

    在学习JavaWeb JSTL与自定义标签时遇到的坑,用的老师给的代码结果直接原地报错:javax.servlet.ServletException: org.apache.jasper.Jasper ...

  3. python---反转链表

    class Node: def __init__(self, data): self.data = data self.next = None class Solution: "" ...

  4. String类为什么被设计成不可变类

    1.享元模式: 1.共享元素模式,也就是说:一个系统中如果有多处用到了相同的一个元素,那么我们应该只存储一份此元素,而让所有地方都引用这一个元素. 2.Java中String就是根据享元模式设计的,而 ...

  5. sql语句——DML

    DML:增删改表中数据 1. 添加数据: * 语法: * insert into 表名(列名1,列名2,...列名n) values(值1,值2,...值n); * 注意: 1. 列名和值要一一对应. ...

  6. Sql语言学习——DDl

    DDL:操作数据库.表 1. 操作数据库:CRUD 1. C(Create):创建 * 创建数据库: * create database 数据库名称; * 创建数据库,判断不存在,再创建: * cre ...

  7. 2021.07.23 P2474 天平(差分约束)

    2021.07.23 P2474 天平(差分约束) [P2474 SCOI2008]天平 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 题意: 已知A,B和每两个点点权,求点权i, ...

  8. 控制Python浮点数输出位数

    技术背景 在Python的一些长效任务中,不可避免的需要向文本文件.二进制文件或者数据库中写入一些数据,或者是在屏幕上输出一些文本,此时如何控制输出数据的长度是需要我们注意的一个问题.比如对于一个二进 ...

  9. 【报错解决】Uncaught TypeError: Failed to execute 'readAsDataURL' on 'FileReader': parameter 1 is not of type 'Blob'.

    项目开发日记-bug多多篇(2) 同时也是 实现一些功能(3) 真的痛苦,写一天代码遇到的bug够我写三天博客. 今天是为了做一个头像功能,具体说是用户上传头像文件并且预览的功能. <div c ...

  10. Python图像处理丨OpenCV+Numpy库读取与修改像素

    摘要:本篇文章主要讲解 OpenCV+Numpy 图像处理基础知识,包括读取像素和修改像素. 本文分享自华为云社区<[Python图像处理] 二.OpenCV+Numpy库读取与修改像素> ...