KingbaseES数据库适配Activiti7 didn't put process definition问题处理过程

发布时间 2023-09-18 16:24:12作者: KINGBASE研究院

一、Activiti介绍

Activiti是一个轻量级的java开源BPMN 2工作流引擎.目前以升级至7.x,支持与springboot2.x集成.

二、项目环境

Spring Boot版本2.2.5

Activiti 版本 7.1.x

源数据库:MySQL 5.7

目标数据库:KinbgaseES V008R006C007B0024

JDBC驱动:Postgre形态的JDBC驱动,postgresql-42.2.9.jar

三、错误信息

此项目为迁移MySQL数据库数据到KingbaseES数据库进行适配,数据迁移完成后Activiti启动报错。

Activiti启动报错信息:

org.activiti.engine.ActivitiException: deployment '01048b5e-79e5-11ed-a2ea-ba4c05fac664' didn't put process definition '010accf0-79e5-11ed-a2ea-ba4c05fac664' in the cache
    at org.activiti.engine.impl.persistence.deploy.DeploymentManager.resolveProcessDefinition(DeploymentManager.java:127)
    at org.activiti.engine.impl.persistence.deploy.DeploymentManager.findDeployedProcessDefinitionById(DeploymentManager.java:76)
    at org.activiti.engine.impl.util.ProcessDefinitionUtil.getBpmnModel(ProcessDefinitionUtil.java:65)
    at org.activiti.engine.impl.cmd.GetBpmnModelCmd.execute(GetBpmnModelCmd.java:41)
    at org.activiti.engine.impl.cmd.GetBpmnModelCmd.execute(GetBpmnModelCmd.java:26)
    at org.activiti.engine.impl.interceptor.CommandInvoker$1.run(CommandInvoker.java:37)
    at org.activiti.engine.impl.interceptor.CommandInvoker.executeOperation(CommandInvoker.java:78)
    at org.activiti.engine.impl.interceptor.CommandInvoker.executeOperations(CommandInvoker.java:57)
    at org.activiti.engine.impl.interceptor.CommandInvoker.execute(CommandInvoker.java:42)
    at org.activiti.engine.impl.interceptor.TransactionContextInterceptor.execute(TransactionContextInterceptor.java:48)
    at org.activiti.engine.impl.interceptor.CommandContextInterceptor.execute(CommandContextInterceptor.java:59)
    at org.activiti.spring.SpringTransactionInterceptor$1.doInTransaction(SpringTransactionInterceptor.java:47)
    at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:140)
    at org.activiti.spring.SpringTransactionInterceptor.execute(SpringTransactionInterceptor.java:45)
    at org.activiti.engine.impl.interceptor.LogInterceptor.execute(LogInterceptor.java:29)
    at org.activiti.engine.impl.cfg.CommandExecutorImpl.execute(CommandExecutorImpl.java:44)
    at org.activiti.engine.impl.cfg.CommandExecutorImpl.execute(CommandExecutorImpl.java:39)
    at org.activiti.engine.impl.RepositoryServiceImpl.getBpmnModel(RepositoryServiceImpl.java:142)
    at org.activiti.runtime.api.model.impl.APIProcessDefinitionConverter.from(APIProcessDefinitionConverter.java:43)
    at org.activiti.runtime.api.model.impl.APIProcessDefinitionConverter.from(APIProcessDefinitionConverter.java:26)
    at org.activiti.runtime.api.model.impl.ListConverter.from(ListConverter.java:28)
    at org.activiti.spring.ProcessDeployedEventProducer.doStart(ProcessDeployedEventProducer.java:55)
    at org.activiti.spring.AbstractActivitiSmartLifeCycle.start(AbstractActivitiSmartLifeCycle.java:68)
    at org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:182)
    at org.springframework.context.support.DefaultLifecycleProcessor.access$200(DefaultLifecycleProcessor.java:53)
    at org.springframework.context.support.DefaultLifecycleProcessor$LifecycleGroup.start(DefaultLifecycleProcessor.java:360)
    at org.springframework.context.support.DefaultLifecycleProcessor.startBeans(DefaultLifecycleProcessor.java:158)
    at org.springframework.context.support.DefaultLifecycleProcessor.onRefresh(DefaultLifecycleProcessor.java:122)
    at org.springframework.context.support.AbstractApplicationContext.finishRefresh(AbstractApplicationContext.java:894)
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.finishRefresh(ServletWebServerApplicationContext.java:162)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:553)
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:141)
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:747)
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:315)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1226)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1215)
    at com.cityOperation.ComDjldPlatformApplication.main(ComDjldPlatformApplication.java:39)

四、问题原因分析

4.1、通过查看Activiti源码得知cachedProcessDefinition==null的时候会发生此错误。

/**
 * Resolving the process definition will fetch the BPMN 2.0, parse it and store the {@link BpmnModel} in memory.
 */
public ProcessDefinitionCacheEntry resolveProcessDefinition(ProcessDefinition processDefinition) {
 String processDefinitionId = processDefinition.getId();
 String deploymentId = processDefinition.getDeploymentId();
 ProcessDefinitionCacheEntry cachedProcessDefinition = processDefinitionCache.get(processDefinitionId);
 if (cachedProcessDefinition == null) {
  CommandContext commandContext = Context.getCommandContext();
  if (commandContext.getProcessEngineConfiguration().isActiviti5CompatibilityEnabled() && 
    Activiti5Util.isActiviti5ProcessDefinition(Context.getCommandContext(), processDefinition)) {

   return Activiti5Util.getActiviti5CompatibilityHandler().resolveProcessDefinition(processDefinition);
  }

  DeploymentEntity deployment = deploymentEntityManager.findById(deploymentId);
  deployment.setNew(false);
  deploy(deployment, null);
  cachedProcessDefinition = processDefinitionCache.get(processDefinitionId);
  if (cachedProcessDefinition == null) {
   throw new ActivitiException("deployment '" + deploymentId + "' didn't put process definition '" + processDefinitionId + "' in the cache");
  }
 }
 return cachedProcessDefinition;
}

Activiti框架DeploymentManager.resolveProcessDefinition方法的作用是根据流程定义的ID或者KEY解析出对应的流程定义对象和BPMN模型对象。这个方法会先从缓存中查找,如果没有找到,就会调用Deployer对象的deploy方法来部署流程定义,并将解析出的对象放入缓存中。这个方法主要是在执行流程实例时,需要根据流程定义的信息来创建流程实例.

4.2、在数据库查询流程定义表

使用以下sql进行查询:

--$1替换为报错的DEPLOYMENT_ID_
select * from ACT_RE_PROCDEF where DEPLOYMENT_ID_ = $1
--替换后的sql为
select * from ACT_RE_PROCDEF where DEPLOYMENT_ID_ = '01048b5e-79e5-11ed-a2ea-ba4c05fac664'

如果查询ACT_RE_PROCDEF流程定义表有数据,那么需要查询数据库日志找出程序查询sql语句:

--数据库日志
2023-05-25 19:47:21 CST [10136]: [19-1] user=system,db=ygf,app=PostgreSQL JDBC Driver,client=10.0.8.248DETAIL:  parameters: $1 = '01048b5e-79e5-11ed-a2ea-ba4c05fac664'
2023-05-25 19:47:21 CST [10136]: [20-1] user=system,db=ygf,app=PostgreSQL JDBC Driver,client=10.0.8.248LOG:  execute <unnamed>: select * from ACT_GE_BYTEARRAY where DEPLOYMENT_ID_ = $1 order by NAME_ asc
2023-05-25 19:47:21 CST [10136]: [21-1] user=system,db=ygf,app=PostgreSQL JDBC Driver,client=10.0.8.248DETAIL:  parameters: $1 = '01048b5e-79e5-11ed-a2ea-ba4c05fac664'
2023-05-25 19:47:21 CST [10136]: [22-1] user=system,db=ygf,app=PostgreSQL JDBC Driver,client=10.0.8.248LOG:  execute <unnamed>: select * 
        from ACT_RE_PROCDEF 
        where DEPLOYMENT_ID_ = $1
          and KEY_ = $2
          and (TENANT_ID_ = '' or TENANT_ID_ is null)
2023-05-25 19:47:21 CST [10136]: [23-1] user=system,db=ygf,app=PostgreSQL JDBC Driver,client=10.0.8.248DETAIL:  parameters: $1 = '01048b5e-79e5-11ed-a2ea-ba4c05fac664', $2 = 'yaotangzhuanxiangkaopingliucheng2'
2023-05-25 19:47:21 CST [10136]: [24-1] user=system,db=ygf,app=PostgreSQL JDBC Driver,client=10.0.8.248LOG:  execute S_2: ROLLBACK
2023-05-25 19:47:21 CST [10131]: [22-1] user=system,db=ygf,app=PostgreSQL JDBC Driver,client=10.0.8.248LOG:  disconnection: session time: 0:00:30.978 user=system database=ygf host=10.0.8.248 port=7005
2023-05-25 19:47:21 CST [10134]: [19-1] user=system,db=ygf,app=PostgreSQL JDBC Driver,client=10.0.8.248LOG:  disconnection: session time: 0:00:27.940 user=system database=ygf host=10.0.8.248 port=7008
2023-05-25 19:47:21 CST [10135]: [11-1] user=system,db=ygf,app=PostgreSQL JDBC Driver,client=10.0.8.248LOG:  disconnection: session time: 0:00:27.890 user=system database=ygf host=10.0.8.248 port=7009
2023-05-25 19:47:21 CST [10136]: [25-1] user=system,db=ygf,app=PostgreSQL JDBC Driver,client=10.0.8.248LOG:  disconnection: session time: 0:00:27.839 user=system database=ygf host=10.0.8.248 port=7010
2023-05-25 19:47:21 CST [10137]: [7-1] user=system,db=ygf,app=PostgreSQL JDBC Driver,client=10.0.8.248LOG:  disconnection: session time: 0:00:27.804 user=system database=ygf host=10.0.8.248 port=7011

--程序查询sql
select * 
        from ACT_RE_PROCDEF 
        where DEPLOYMENT_ID_ = $1
          and KEY_ = $2
          and (TENANT_ID_ = '' or TENANT_ID_ is null)
2023-05-25 19:47:21 CST [10136]: [23-1] user=system,db=ygf,app=PostgreSQL JDBC Driver,client=10.0.8.248DETAIL:  parameters: $1 = '01048b5e-79e5-11ed-a2ea-ba4c05fac664', $2 = 'yaotangzhuanxiangkaopingliucheng2'

使用数据库日志找到的程序查询sql在数据库进行查询发现无数据返回,通过定位发现是由于TENANT_ID_ = '' or TENANT_ID_ is null此条件导致。

4.3、查询数据库参数配置

show ora_input_emptystr_isnull ;
 ORA_INPUT_EMPTYSTR_ISNULL 
---------------------------
 on
(1 row)

--此参数作用是对输入空字符串时的处理参数:
--on表示将输入的空字符串作为null值处理、off表示不处理

在KingbaseES数据库,ora_input_emptystr_isnull=on的场景:空字符串''使用=''数据库会将输入的空字符串作为null值处理,作为null值的时候不能匹配到数据,而在数据库里面TENANT_ID_字段数据值就是'',程序启动时查询流程定义表没有返回,导致的异常。

五、解决方法

修改数据库ora_input_emptystr_isnull参数为off。