前言

在使用开源流程引擎(如: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. vue项目中返回之前页面数据不刷新的问题

    利用vue做项目的时候会有让用户选择当前页面的某些数据,然后再跳到下一页,而下一页是根据上一页的数据来的,有时候选择了上一页的不同选项,下一页的数据可能还是之前的 这个就属于vue的数据获取问题 解决 ...

  2. animate.css使用

    解决 使用jquery单纯添加类不能出现动画 使用jQuery向元素中添加类制作动画的时候,需要使用setTimeout实现,因为动画需要从一个状态到另外一个状态!时间设置为0

  3. Java报错:org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.sirifeng.babytun.dao.GoodsDAO.findById

    前言 最近学vue学得差不多了,想来搭个项目实战一下,结果刚开始搭建SSM框架的时候就来到了我们最喜欢的debug环节 org.apache.ibatis.binding.BindingExcepti ...

  4. Struts2-day1总结

    1.Struts2的基本执行流程(详见我的博客) 2.Struts2的分模块开发 如果有多个项目的Struts.xml写在一起,容易造成数据混乱,所以可以使用分模块的方法,在项目下新建*.xml配置文 ...

  5. 怎么快速找出帝国CMS数据库配置文件路径及迁移网站后修改技巧!

    首先,我们要了解一下帝国CMS整个目录结构,只有了解清楚结构,我们才有可能快速找到自己想要的文件,比如:帝国CMS数据库配置文件路径! 帝国CMS目录结构介绍 / 系统根目录├d/ 附件和数据存放目录 ...

  6. 另类终端「GitHub 热点速览 v.22.15」

    作者:HelloGitHub-小鱼干 除了编译器之外,终端也是我们日常打交道的软件之一.但,你用它看过股票吗?OpenBBTerminal 不仅能让你看股票,还能让你用科学的方法进行股票投资.说到投资 ...

  7. VMware下ubuntu 20.04扩容/磁盘

    最近搞zabbix监控,发现搭建的监控server主机磁盘告警.提示/超过阈值80%. 有实在VMware软件下的虚拟机,首先我就是给虚机磁盘增加容量. 增加后发现没什么改变,看来还需要其他操作. 在 ...

  8. Mybatis结果集映射问题

    之前的数据库图简单都是纯小写格式,这一次做项目为了显得正规一些,模拟实际的情况,采用了驼峰命名的规则,这时候就遇到了结果匹配的问题. 之前只要 <select id="select&q ...

  9. SSM整合_年轻人的第一个增删改查_查找

    写在前面 SSM整合_年轻人的第一个增删改查_基础环境搭建 SSM整合_年轻人的第一个增删改查_查找 SSM整合_年轻人的第一个增删改查_新增 SSM整合_年轻人的第一个增删改查_修改 SSM整合_年 ...

  10. django orm 更新数据时间不自动更新问题

    gmt_create自动添加auto_now_add:gmt_modify自动更新auto_now class CommonInfo(models.Model): """ ...