Flowable执行完毕的流程查找方法

目录
  • 正文
  • 1. 历史流程信息
  • 2. 历史任务查询
  • 3. 历史活动查询
  • 4. 历史变量查询
  • 5. 历史日志查询
  • 6. 历史权限查询
  • 7. 自定义查询 SQL
  • 8. 历史数据记录级别

正文

@[toc]
在之前的文章中松哥和小伙伴们聊过,正在执行的流程信息是保存在以 ACT_RU_ 为前缀的表中,执行完毕的流程信息则保存在以 ACT_HI_ 为前缀的表中,也就是流程历史信息表,当然这个历史信息表继续细分的话,还有好多种,今天我们就来聊一聊这个话题。

假设我有如下一个流程:

当这个流程执行完毕后,以 ACT_RU_ 为前缀的表中的数据均已清空,现在如果想查看刚刚执行过的流程信息,我们就得去以 ACT_HI_ 为前缀的表中。

1. 历史流程信息

历史流程信息查看,方式如下:

@Test
void test05() {
    List<HistoricProcessInstance> list = historyService.createHistoricProcessInstanceQuery().finished().list();
    for (HistoricProcessInstance hpi : list) {
        logger.info("name:{},startTime:{},endTime:{}",hpi.getName(),hpi.getStartTime(),hpi.getEndTime());
    }
}

调用的时候执行的 finished() 方法表示查询已经执行完毕的流程信息(从这里也可以看出,对于未执行完毕的流程信息也会保存在历史表中)。

我们来看下这个查询对应的 SQL,如下:

SELECT RES.* , DEF.KEY_ as PROC_DEF_KEY_, DEF.NAME_ as PROC_DEF_NAME_, DEF.VERSION_ as PROC_DEF_VERSION_, DEF.DEPLOYMENT_ID_ as DEPLOYMENT_ID_ from ACT_HI_PROCINST RES left outer join ACT_RE_PROCDEF DEF on RES.PROC_DEF_ID_ = DEF.ID_ WHERE RES.END_TIME_ is not NULL order by RES.ID_ asc

从这个 SQL 中可以看到,这个查询本质上就是查询的 ACT_HI_PROCINST 表。如下图:

如果我们在查询的时候不限制流程是否执行完毕,那么我们的查询方法如下:

@Test
void test05() {
    List<HistoricProcessInstance> list = historyService.createHistoricProcessInstanceQuery().list();
    for (HistoricProcessInstance hpi : list) {
        logger.info("name:{},startTime:{},endTime:{}",hpi.getName(),hpi.getStartTime(),hpi.getEndTime());
    }
}

对应的查询 SQL 如下:

SELECT RES.* , DEF.KEY_ as PROC_DEF_KEY_, DEF.NAME_ as PROC_DEF_NAME_, DEF.VERSION_ as PROC_DEF_VERSION_, DEF.DEPLOYMENT_ID_ as DEPLOYMENT_ID_ from ACT_HI_PROCINST RES left outer join ACT_RE_PROCDEF DEF on RES.PROC_DEF_ID_ = DEF.ID_ order by RES.ID_ asc

和前面的 SQL 相比,后面的 SQL 少了 WHERE RES.END_TIME_ is not NULL 条件,也就是说,判断一个流程是否执行完毕,就看它的 END_TIME_ 是否为空,不为空就表示流程已经执行结束了,为空就表示流程尚在执行中。

2. 历史任务查询

刚刚我们查询的是历史流程,接下来我们来看下历史任务,也就是查询一个流程中执行过的 Task 信息,如下表示查询所有的历史流程任务:

@Test
void test06() {
    List<HistoricTaskInstance> list = historyService.createHistoricTaskInstanceQuery().list();
    for (HistoricTaskInstance hti : list) {
        logger.info("name:{},assignee:{},createTime:{},endTime:{}",hti.getName(),hti.getAssignee(),hti.getCreateTime(),hti.getEndTime());
    }
}

这个查询对应的 SQL 如下:

SELECT RES.* from ACT_HI_TASKINST RES order by RES.ID_ asc

可以看到,历史任务表就是 ACT_HI_TASKINST,如下图:

当然,这里还有很多其他的玩法,例如查询某一个流程已经执行完毕的历史任务,如下:

@Test
void test07() {
    List<HistoricProcessInstance> instanceList = historyService.createHistoricProcessInstanceQuery().list();
    for (HistoricProcessInstance hpi : instanceList) {
        List<HistoricTaskInstance> list = historyService.createHistoricTaskInstanceQuery().processInstanceId(hpi.getId()).finished().list();
        for (HistoricTaskInstance hti : list) {
            logger.info("name:{},assignee:{},createTime:{},endTime:{}", hti.getName(), hti.getAssignee(), hti.getCreateTime(), hti.getEndTime());
        }
    }
}

这个里边的查询历史任务的 SQL 如下:

SELECT RES.* from ACT_HI_TASKINST RES WHERE RES.PROC_INST_ID_ = ? and RES.END_TIME_ is not null order by RES.ID_ asc

可以看到,跟前面相比,多了两个条件:

  • 流程实例 ID
  • 流程结束时间不为 null

从这里也可以看出来,这个 finish 方法的执行逻辑跟我们前面讲的是一样的。

3. 历史活动查询

历史任务就是各种 Task,历史活动则包括跟多内容,像开始/结束节点,连线等等这些信息都算是活动,这个在之前的文章中松哥已经和大家介绍过了。

查询代码如下:

@Test
void test08() {
    List<HistoricActivityInstance> list = historyService.createHistoricActivityInstanceQuery().list();
    for (HistoricActivityInstance hai : list) {
        logger.info("name:{},startTime:{},assignee:{},type:{}",hai.getActivityName(),hai.getStartTime(),hai.getAssignee(),hai.getActivityType());
    }
}

这个查询对应的 SQL 如下:

SELECT RES.* from ACT_HI_ACTINST RES order by RES.ID_ asc

可以看到,ACT_HI_ACTINST 表中保存了历史活动信息。

4. 历史变量查询

查询流程执行的历史变量,方式如下:

@Test
void test09() {
    HistoricProcessInstance pi = historyService.createHistoricProcessInstanceQuery().singleResult();
    List<HistoricVariableInstance> list = historyService.createHistoricVariableInstanceQuery().processInstanceId(pi.getId()).list();
    for (HistoricVariableInstance hvi : list) {
        logger.info("name:{},type:{},value:{}", hvi.getVariableName(), hvi.getVariableTypeName(), hvi.getValue());
    }
}

这个查询对应的 SQL 如下:

SELECT RES.* from ACT_HI_VARINST RES WHERE RES.PROC_INST_ID_ = ? order by RES.ID_ asc

可以看到流程的历史变量信息保存在 ACT_HI_VARINST 表中。

5. 历史日志查询

有的小伙伴看到日志这两个字可能会觉得奇怪,咦?流程执行还有日志吗?没听说过呀!

其实历史日志查询就是前面那几种的一个集大成者,用法如下:

@Test
void test10() {
    HistoricProcessInstance pi = historyService.createHistoricProcessInstanceQuery().singleResult();
    ProcessInstanceHistoryLog historyLog = historyService.createProcessInstanceHistoryLogQuery(pi.getId())
            //包括历史活动
            .includeActivities()
            //包括历史任务
            .includeTasks()
            //包括历史变量
            .includeVariables()
            .singleResult();
    logger.info("id:{},startTime:{},endTime:{}", historyLog.getId(), historyLog.getStartTime(), historyLog.getEndTime());
    List<HistoricData> historicData = historyLog.getHistoricData();
    for (HistoricData data : historicData) {
        if (data instanceof HistoricActivityInstance) {
            HistoricActivityInstance hai = (HistoricActivityInstance) data;
            logger.info("name:{},type:{}", hai.getActivityName(), hai.getActivityType());
        }
        if (data instanceof HistoricTaskInstance) {
            HistoricTaskInstance hti = (HistoricTaskInstance) data;
            logger.info("name:{},assignee:{}", hti.getName(), hti.getAssignee());
        }
        if (data instanceof HistoricVariableInstance) {
            HistoricVariableInstance hvi = (HistoricVariableInstance) data;
            logger.info("name:{},type:{},value:{}", hvi.getVariableName(), hvi.getVariableTypeName(), hvi.getValue());
        }
    }
}

这个里边,首先是查询基本的流程日志信息,这个本质上就是查询历史流程实例信息,对应的 SQL 如下:

select RES.*, DEF.KEY_ as PROC_DEF_KEY_, DEF.NAME_ as PROC_DEF_NAME_, DEF.VERSION_ as PROC_DEF_VERSION_, DEF.DEPLOYMENT_ID_ as DEPLOYMENT_ID_ from ACT_HI_PROCINST RES left outer join ACT_RE_PROCDEF DEF on RES.PROC_DEF_ID_ = DEF.ID_ where PROC_INST_ID_ = ?

接下来我写了三个 include,每一个 include 都对应一句 SQL:

includeActivities 对应的 SQL 如下:

SELECT RES.* from ACT_HI_ACTINST RES WHERE RES.PROC_INST_ID_ = ? order by RES.ID_ asc

includeTasks 对应的 SQL 如下:

SELECT RES.* from ACT_HI_TASKINST RES WHERE RES.PROC_INST_ID_ = ? order by RES.ID_ asc

includeVariables 对应的 SQL 如下:

SELECT RES.* from ACT_HI_VARINST RES WHERE RES.PROC_INST_ID_ = ? order by RES.ID_ asc

最终查询完成后,调用 getHistoricData 方法可以查看这些额外的数据,List 集合中存放的 HistoricData 也分为不同的类型:

  • includeActivities 方法对应最终查询出来的类型是 HistoricActivityInstance。
  • includeTasks 方法对应最终查询出来的类型是 HistoricTaskInstance。
  • includeVariables 方法对应最终查询出来的类型是 HistoricVariableInstance。

在遍历的时候通过类型判断去查看具体是哪一种变量类型。

综上,这个历史日志查询其实就是一个集大成者。

6. 历史权限查询

这个是用来查询流程或者任务的处理人,例如查询流程的处理人,方式如下:

@Test
void test11() {
    HistoricProcessInstance pi = historyService.createHistoricProcessInstanceQuery().singleResult();
    List<HistoricIdentityLink> links = historyService.getHistoricIdentityLinksForProcessInstance(pi.getId());
    for (HistoricIdentityLink link : links) {
        logger.info("userId:{}",link.getUserId());
    }
}

这个是查询流程对应的处理人,对应的 SQL 如下:

select * from ACT_HI_IDENTITYLINK where PROC_INST_ID_ = ?

如果想查询任务的处理人,对应的方式如下:

@Test
void test12() {
    String taskName = "提交请假申请";
    HistoricTaskInstance hti = historyService.createHistoricTaskInstanceQuery().taskName(taskName).singleResult();
    List<HistoricIdentityLink> links = historyService.getHistoricIdentityLinksForTask(hti.getId());
    for (HistoricIdentityLink link : links) {
        logger.info("{} 任务的处理人是 {}",taskName,link.getUserId());
    }
}

这个查询对应的 SQL 如下:

select * from ACT_HI_IDENTITYLINK where TASK_ID_ = ?

和前面的相比,其实就多了一个查询条件 TASK_ID_

7. 自定义查询 SQL

和前面讲的很多查询类似,当我们弄懂了每一个历史查询的 API 操作的是哪一个数据表,就会发现,历史数据的查询,也可以自定义 SQL。

举个例子和小伙伴们看下,例如查询某一个流程已经执行完毕的历史任务:

@Test
void test13() {
    List<HistoricProcessInstance> instanceList = historyService.createHistoricProcessInstanceQuery().list();
    for (HistoricProcessInstance hpi : instanceList) {
        List<HistoricTaskInstance> list = historyService.createNativeHistoricTaskInstanceQuery()
                .sql("SELECT RES.* from ACT_HI_TASKINST RES WHERE RES.PROC_INST_ID_ = #{pid} and RES.END_TIME_ is not null order by RES.ID_ asc")
                .parameter("pid",hpi.getId()).list();
        for (HistoricTaskInstance hti : list) {
            logger.info("name:{},assignee:{},createTime:{},endTime:{}", hti.getName(), hti.getAssignee(), hti.getCreateTime(), hti.getEndTime());
        }
    }
}

flowable 底层是 MyBatis,所有 SQL 中参数的传递形式和 MyBatis 一致。

8. 历史数据记录级别

Flowable 需要记录哪些历史数据,有一个日志级别用来描述这个事情,默认有四种级别:

  • None: 这个表示不存储任何历史信息,好处是流程执行的时候效率会比较快,坏处是流程执行结束后,看不到曾经执行过的流程信息了。
  • Activity: 这个会存储所有流程实例和活动实例,在流程实例结束时,顶级流程实例变量的最新值将复制到历史变量实例中,不会存储详细信息。
  • Audit: 在 Activity 的基础上,还会存储历史详细信息,包括权限信息等。默认的日志记录级别即次。
  • Full: 这个是在 Audit 的基础上,还会存储变量的变化信息,这会记录大量的数据,也会导致流程执行变慢。

一共就这四种级别,在 Spring Boot 项目中,如果我们想要配置这个日志记录的级别,其实非常方便,直接在 application.properties 中进行配置即可,如下:

flowable.history-level=none

配置加了这个配置,我们随便启动一个流程,然后去查询 ACT_HI_ 系列的表,发现都是空的,没有数据。

如果我们将历史日志记录的级别改为 activity,那么就会记录下来流程信息以及活动信息,但是像执行的 Task 这些信息都是没有的(ACT_HI_TASKINST),包括流程参与者的信息(ACT_HI_IDENTITYLINK)等都不会记录下来。

如果我们将历史日志记录的级别改为 audit,则上面提到的这几种日志就都会记录下来。但是 ACT_HI_DETAIL 表还是空的,详细一个流程变量的变化过程不会被记录下来。

如果我们将日志记录级别改为 full,那么将会记录下更多的信息。ACT_HI_DETAIL 表中会记录下流程变量的详细信息。

以上就是Flowable执行完毕的流程查找方法的详细内容,更多关于Flowable执行完毕流程查找的资料请关注我们其它相关文章!

(0)

相关推荐

  • 绘制flowable 流程图的Vue 库使用详解

    目录 引言 workflow-bpmn-modeler 注册 bpmnModeler 组件 muheflow-bpmn-modeler 引言 之前松哥发了一篇文章和小伙伴们介绍了前端的 bpmn.js 这个库,利用这个库我们可以自己将绘制流程图的功能嵌入到我们的项目中. 然而,这个库默认是给 Camunda 设计的,所以画出来的流程图导出来的 XML 文件无法直接使用,必须要做一些深度定制,才能将 XML 文件转为 Flowable 流程引擎可用的 XML 文件.这个深度定制太太太麻烦了. 所以

  • Flowable 设置流程变量的四种方式详解

    目录 引言 1. 为什么需要流程变量 2. 流程变量的分类 3. 全局流程变量 3.1 启动时设置 3.2 通过 Task 设置 3.3 完成任务时设置 3.4 通过流程设置 4. 本地流程变量 4.1 通过 Task 设置 5. 临时流程变量 引言 在之前的文章中,松哥也有和小伙伴们使用过流程变量,然而没有和大家系统的梳理过流程变量的具体玩法以及它对应的数据表详情,今天我们就来看看 Flowable 中流程变量的详细玩法. 1. 为什么需要流程变量 首先我们来看看为什么需要流程变量. 举一个简

  • Tensorflow高性能数据优化增强工具Pipeline使用详解

    目录 安装方法 功能 高级用户部分 用例1,为训练创建数据Pipeline 用例2,为验证创建数据Pipeline 初学者部分 Keras 兼容性 配置 增强: GridMask MixUp RandomErase CutMix Mosaic CutMix , CutOut, MixUp Mosaic Grid Mask 安装方法 给大家介绍一个非常好用的TensorFlow数据pipeline工具. 高性能的Tensorflow Data Pipeline,使用SOTA的增强和底层优化. pi

  • 图解 Kotlin SharedFlow 缓存系统及示例详解

    目录 前言 replay extraBufferCapacity onBufferOverflow SharedFlow Buffer 前言 Kotlin 为我们提供了两种创建“热流”的工具:StateFlow 和 SharedFlow.StateFlow 经常被用来替代 LiveData 充当架构组件使用,所以大家相对熟悉.其实 StateFlow 只是 SharedFlow 的一种特化形式,SharedFlow 的功能更强大.使用场景更多,这得益于其自带的缓存系统,本文用图解的方式,带大家更

  • Flow如何解决背压问题的方法详解

    目录 前言 关于背压(BackPressure) 背压问题是什么 定义背压策略 Flow的背压机制 模拟背压问题 背压处理方式 使用buffer进行缓存收集 使用conflate解决 使用collectLatest解决 小结 前言 随着时间的推移,越来越多的主流应用已经开始全面拥抱Kotlin,协程的引入,Flow的诞生,给予了开发很多便捷,作为协程与响应式编程结合的流式处理框架,一方面它简单的数据转换与操作符,没有繁琐的操作符处理,广受大部分开发的青睐,另一方面它并没有响应式编程带来的背压问题

  • Flowable执行完毕的流程查找方法

    目录 正文 1. 历史流程信息 2. 历史任务查询 3. 历史活动查询 4. 历史变量查询 5. 历史日志查询 6. 历史权限查询 7. 自定义查询 SQL 8. 历史数据记录级别 正文 @[toc]在之前的文章中松哥和小伙伴们聊过,正在执行的流程信息是保存在以 ACT_RU_ 为前缀的表中,执行完毕的流程信息则保存在以 ACT_HI_ 为前缀的表中,也就是流程历史信息表,当然这个历史信息表继续细分的话,还有好多种,今天我们就来聊一聊这个话题. 假设我有如下一个流程: 当这个流程执行完毕后,以 

  • layer弹出的iframe层在执行完毕后关闭当前弹出层的方法

    如下所示: 这种弹出层在ajax执行完添加房间的动作后在回调里写 var index = parent.layer.getFrameIndex(window.name); setTimeout(function(){parent.layer.close(index)}, 1000); 1秒后,当前窗口就会关闭了 以上这篇layer弹出的iframe层在执行完毕后关闭当前弹出层的方法就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持我们.

  • springboot项目如何打war包流程的方法

    目前,前后端分离的架构已成主流,因此使用springboot构建应用是非常快速的,项目发布到服务器上的时候,只需要打成一个jar包,然后通过命令 : java -jar jar包名称即可启动服务了: 但在有些场景下,比如部署到外部的服务器的时候,客户方指定要以tomcat的形式启动服务的时候,即需要由服务提供方提供一个war包的时候,该怎么办呢? 一开始没有查找资料,自己差点准备使用传统的ssm框架改造服务的,后来思考和参考相关资料后,发现只需要挤出简单的配置和改造就可以做到,下面是记载将spr

  • 判断Threading.start新线程是否执行完毕的实例

    新写自己的Threading类 class MyThread(threading.Thread):#我的Thread类 判断流程结束没 用于os shell命令是否执行判断 def __init__(self,func = ""):#输入待执行函数名 我执行的函数没有参数就没有加args输入了 threading.Thread.__init__(self) self.func = func self.result = 1#未完成为1 标志位 # 调用start自动执行的函数 def r

  • 解析spring加载bean流程的方法

    spring作为目前我们开发的基础框架,每天的开发工作基本和他形影不离,作为管理bean的最经典.优秀的框架,它的复杂程度往往令人望而却步.不过作为朝夕相处的框架,我们必须得明白一个问题就是spring是如何加载bean的,我们常在开发中使用的注解比如@Component.@AutoWired.@Socpe等注解,Spring是如何解析的,明白这些原理将有助于我们更深刻的理解spring.需要说明一点的是spring的源码非常精密.复杂,限于篇幅的关系,本篇博客不会细致的分析源码,会采取抽丝剥茧

  • 详解PHP的执行原理和流程

    简介 先看看下面这个过程: • 我们从未手动开启过PHP的相关进程,它是随着Apache的启动而运行的: • PHP通过mod_php5.so模块和Apache相连(具体说来是SAPI,即服务器应用程序编程接口): • PHP总共有三个模块:内核.Zend引擎.以及扩展层: • PHP内核用来处理请求.文件流.错误处理等相关操作: • Zend引擎(ZE)用以将源文件转换成机器语言,然后在虚拟机上运行它: • 扩展层是一组函数.类库和流,PHP使用它们来执行一些特定的操作.比如,我们需要mysq

  • Spring @Transaction 注解执行事务的流程

    前言 相信小伙伴一定用过 @Transaction 注解,那 @Transaction 背后的秘密又知道多少呢? Spring 是如何开启事务的?又是如何进行提交事务和关闭事务的呢? 画图猜测 在开始 debug 阅读源码之前,小伙伴们应该已经知道 MySQL 是如何开启事务的. 因此可以得出猜测: 那下面跟着源码一起读一读,Spring 的 @Transaction 注解是如何执行事务逻辑的? Spring 事务执行流程 开启事务 这里使用的是 Spring Boot + MySQL + Dr

  • jQuery选择器总结之常用元素查找方法

    选择器并没有一个固定的定义,在某种程度上说,jQuery的选择器和样式表中的选择器十分相似.选择器具有如下特点: 1.简化代码的编写 2.隐式迭代 3.无须判断对象是否存在 jQuery 的选择器可谓之强大无比,这里简单地总结一下常用的元素查找方法 $("#myELement") 选择id值等于myElement的元素,id值不能重复在文档中只能有一个id值是myElement所以得到的是唯一的元素 $("div") 选择所有的div标签元素,返回div元素数组 $

  • javascript封装addLoadEvent实现页面同时加载执行多个函数的方法

    本文实例讲述了javascript封装addLoadEvent实现页面同时加载执行多个函数的方法.分享给大家供大家参考,具体如下: 如果想同时执行多个函数,可以将这些函数放入一个数组中,然后在onload事件里循环数组并执行,或者使用另一个方便的函数addLoadEvent: function addLoadEvent(func) { var oldonload = window.onload; if (typeof window.onload != 'function') { window.o

  • Linux中文件查找方法大全

    每一种操作系统都是由成千上万个不同种类的文件所组成的.其中有系统本身自带的文件,用户自己的文件,还有共享文件等等.我们有时候经常忘记某份文件放在硬盘中的哪个地方.在微软的Windows操作系统中要查找一份文件是相当简单的事情,只要在桌面上点击"开始"-"搜索"中就能按照各种方式在本地硬盘上,局域网络,甚至在INTERNET上查找各种文件,文档. 可是使用Linux的用户就没有那么幸运了,在Linux上查找某个文件确实是一件比较麻烦的事情.毕竟在Linux中需要我们使

随机推荐