1. 程式人生 > 其它 >開源流程引擎Camunda BPM如何擴充套件資料庫表

開源流程引擎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

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原生資料操作機制。