大道至简,知易行难
广阔天地,大有作为

Activiti 5.x HistoricTaskInstanceQuery的count方法缺陷

近日,发现某生产系统中使用基础框架所引入的Activiti 5.21.0版本中,HistoricTaskInstanceQuery的count方法存在缺陷(其他的count有没有缺陷没有细看),表现为HistoricTaskInstanceQuery的listPage和count得到的结果差异巨大,且count的结果明显不正确:

HistoricTaskInstanceQuery的count结果不正确

HistoricTaskInstanceQuery的count结果不正确

上述代码的pattern在产品系统中被大量用到,即通过listPage和count向前台返回分页查询的结果及总记录数,供前台展示及后续分页。

此前,HistoricTaskInstanceQuery的查询条件相对简单,并未发现任何问题;而当针对某个新需求,增加了针对流程实例变量、包含OR等复杂查询条件后,就暴露出了上述的问题。显然,count的结果明显是诡异的(实际结果总数量为21条、count的结果错误,整个任务历史表才2000条数据)。搜遍全网无结果,无奈看源码,发现确实有BUG。

开启Activiti日志,可以观察到listPage和count对应的实际查询语句:

HistoricTaskInstanceQuery生成的实际查询语句

HistoricTaskInstanceQuery生成的实际查询语句

前者为DB2的分页查询写法。可见,HistoricTaskInstanceQuery拼接查询条件是通过多个LEFT JOIN实现的,除了DB2的分页逻辑外,这两个查询的核心逻辑基本是一样的,唯独在COUNT中缺少了应有的DISTINCT谓词,手拼SQL后验证确实如此。观察源码的实现:

Activiti中list和count的实现

Activiti中list和count的实现

最终调用到了HistoricTaskInstance.xml中:

Activiti 5.x 部分Query的count方法错误

Activiti 5.x 部分Query的count方法错误

接下来确认该缺陷的影响范围。经查,目前Activiti最新版本7.x中已经修复了该问题,其解决方法恰恰是增加了DISTINCT。我们可以在GitHub的repo中确认:

最新版本的Activiti中已经修复了该问题

最新版本的Activiti中已经修复了该问题

显然,当存在复杂查询拼接出的LEFT JOIN时,DISTINCT是不可以少的。但还是有某些无知的小白竟然想通过删除DISTINCT来提高查询效率:

想删掉DISTINCT的无知小白

想删掉DISTINCT的无知小白

5.22.0是Activiti 5.x的最高版本,根据Release Note显示也没有修复该问题;而查看5.22.0的源码也确实没有解决该问题。尽管最新7.x的Activiti已经修复了该问题,但在GitHub中翻了半天也没有翻到到底是在哪个版本中修复的这个问题,翻了下Release Note也没有翻到,那就管不了这么多了。

由于目前生产系统中已经有大量的数据,贸然提高Activiti大版本需要执行upgrade脚本,而又难以在测试环境验证upgrade脚本的正确性,因此不能通过简单升级Activiti版本的方式来解决这个问题。退而求其次,由于只需要修改有问题的Mapper文件,因此我们可以直接修改activiti-engine-5.21.0.jar中的HistoricTaskInstance.xml及pom.xml,构造一个activiti-engine-5.21.1.jar并上传至私有Maven仓库中即可。

转载时请保留出处,违法转载追究到底:进城务工人员小梅 » Activiti 5.x HistoricTaskInstanceQuery的count方法缺陷

分享到:更多 ()

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址