Camunda BPM如何扩展数据库表

前言

在使用开源流程引擎(如: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

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="org.camunda.bpm.engine.impl.persistence.entity.ProcessCatalogEntity">

  <insert id="insertProcessCatalog" parameterType="org.camunda.bpm.engine.impl.persistence.entity.ProcessCatalogEntity">
    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})
  </insert>

  <update id="updateProcessCatalog" parameterType="org.camunda.bpm.engine.impl.persistence.entity.ProcessCatalogEntity">
    update ${prefix}ACT_RE_PROC_CATALOG
    <set>
      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}
    </set>
    where ID_= #{id, jdbcType=VARCHAR}
  </update>

  <delete id="deleteProcessCatalog" parameterType="string">
    delete from ${prefix}ACT_RE_PROC_CATALOG where ID_ = #{id}
  </delete>


  <resultMap id="processCatalogResultMap" type="org.camunda.bpm.engine.impl.persistence.entity.ProcessCatalogEntity">
    <id property="id" column="ID_" jdbcType="VARCHAR" />
    <result property="catalogCode" column="CATALOG_CODE_" jdbcType="VARCHAR" />
    <result property="catalogName" column="CATALOG_NAME_" jdbcType="VARCHAR" />
    <result property="parentId" column="PARENT_ID_" jdbcType="VARCHAR" />
    <result property="createTime" column="CREATE_TIME_" jdbcType="TIMESTAMP"/>
    <result property="tenantId" column="TENANT_ID_" jdbcType="VARCHAR" />
  </resultMap>


  <select id="selectProcessCatalog" parameterType="string" resultMap="processCatalogResultMap">
    select * from ${prefix}ACT_RE_PROC_CATALOG where ID_ = #{id}
  </select>



  <select id="selectProcessCatalogsByQueryCriteria" parameterType="org.camunda.bpm.engine.impl.ProcessCatalogQueryImpl" resultMap="processCatalogResultMap">
  	<include refid="org.camunda.bpm.engine.impl.persistence.entity.Commons.bindOrderBy"/>
    ${limitBefore}
    select ${distinct} RES.*
    ${limitBetween}
    <include refid="selectProcessCatalogsByQueryCriteriaSql"/>
    ${orderBy}
    ${limitAfter}
  </select>

  <select id="selectProcessCatalogCountByQueryCriteria" parameterType="org.camunda.bpm.engine.impl.ProcessCatalogQueryImpl" resultType="long">
    select count(distinct RES.ID_)
    <include refid="selectProcessCatalogsByQueryCriteriaSql"/>
  </select>

  <sql id="selectProcessCatalogsByQueryCriteriaSql">

    from ${prefix}ACT_RE_PROC_CATALOG RES

    <include refid="org.camunda.bpm.engine.impl.persistence.entity.AuthorizationEntity.authCheckJoin" />

    <where>
      <if test="id != null">
        RES.ID_ = #{id}
      </if>
      <if test="catalogCode != null">
        and RES.CATALOG_CODE_ = #{catalogCode}
      </if>
      <if test="catalogName != null">
        and RES.CATALOG_NAME_ = #{catalogName}
      </if>
      <if test="parentId != null">
        and RES.PARENT_ID_ = #{parentId}
      </if>
      <if test="createTimeBefore != null">
        and RES.CREATE_TIME_ &lt; #{createTimeBefore}
      </if>
      <if test="createTimeAfter != null">
        and RES.CREATE_TIME_ &gt; #{createTimeAfter}
      </if>
      <if test="isTenantIdSet">
        <if test="tenantIds != null &amp;&amp; tenantIds.length > 0">
          and ( RES.TENANT_ID_ in
          <foreach item="tenantId" index="index" collection="tenantIds"
                   open="(" separator="," close=")">
            #{tenantId}
          </foreach>
          <if test="includeDeploymentsWithoutTenantId">
            or RES.TENANT_ID_ is null
          </if>
          )
        </if>
        <if test="tenantIds == null">
          and RES.TENANT_ID_ is null
        </if>
      </if>

      <include refid="org.camunda.bpm.engine.impl.persistence.entity.AuthorizationEntity.queryAuthorizationCheck" />
      <include refid="org.camunda.bpm.engine.impl.persistence.entity.TenantEntity.queryTenantCheck" />

    </where>
  </sql>

  <select id="selectProcessCatalogsByProcdef" parameterType="org.camunda.bpm.engine.impl.db.ListQueryParameterObject" resultMap="processCatalogResultMap">
    select *
    from ${prefix}ACT_RE_PROC_CATALOG
    where PARENT_ID_ = #{parameter.parentId}
    <if test="parameter.catalogCode != null">
      and CATALOG_CODE_ = #{parameter.catalogCode}
    </if>
    <if test="parameter.catalogName != null">
      and CATALOG_NAME_ = #{parameter.catalogName}
    </if>
    <if test="parameter.tenantId == null">
      and TENANT_ID_ is null
    </if>
    <if test="parameter.tenantId != null">
      and TENANT_ID_ = #{parameter.tenantId}
    </if>
    order by CATALOG_CODE_ asc
  </select>

  <!-- mysql specific -->
  <select id="selectProcessCatalogsByQueryCriteria_mysql" parameterType="org.camunda.bpm.engine.impl.ProcessCatalogQueryImpl" resultMap="processCatalogResultMap">
    <include refid="org.camunda.bpm.engine.impl.persistence.entity.Commons.bindOrderBy"/>
    ${limitBefore}
    select ${distinct} RES.*
    ${limitBetween}
    <include refid="selectProcessCatalogsByQueryCriteriaSql"/>
   	${orderBy}
    ${limitAfter}
  </select>

  <!-- mysql specific -->
  <select id="selectProcessCatalogCountByQueryCriteria_mysql" parameterType="org.camunda.bpm.engine.impl.ProcessCatalogQueryImpl" resultType="long">
    select count(distinct RES.ID_)
    <include refid="selectProcessCatalogsByQueryCriteriaSql"/>
  </select>

</mapper>

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<ProcessCatalog> 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<ProcessCatalogEntity> findProcessCatalogsByParentId(String parentId) {
    Map<String, Object> parameters = new HashMap<String, Object>();
    parameters.put("parentId", parentId);
    ListQueryParameterObject parameterObject = new ListQueryParameterObject();
    parameterObject.setParameter(parameters);
    List<ProcessCatalogEntity> 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<ProcessCatalog> 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原生数据操作机制,便于后续深度定制和排错,技术支持:http://www.yunchengxc.com。

评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大龄码农有梦想

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值