@[toc]上一篇宋哥和他的小伙伴们聊过。正在执行的进程信息存放在以ACT_RU_为前缀的表中,完成的进程信息存放在以ACT_HI_为前缀的表中,其中,就是进程历史信息表。当然,如果将这张历史信息表进一步细分,还有很多其他的类型。今天我们就来聊聊这个话题。假设我有如下流程:流程执行时,将ACT_RU_前缀的表中的数据清空。现在如果我们要查看刚刚执行过的进程信息,就得去ACT_HI_前缀的表。1、通过以下方式查看历史进程信息:@Testvoidtest05(){Listlist=historyService.createHistoricProcessInstanceQuery().finished().list();对于(HistoricProcessInstancehpi:list){logger.info("name:{},startTime:{},endTime:{}",hpi.getName(),hpi.getStartTime(),hpi.getEndTime());}}调用时执行的finished()方法代表了查询已经执行过的流程信息(从这里也可以看出,没有执行过的流程信息也会保存在历史表中)。我们看一下这个查询对应的SQL,如下:SELECTRES.*,DEF.KEY_asPROC_DEF_KEY_,DEF.NAME_asPROC_DEF_NAME_,DEF.VERSION_asPROC_DEF_VERSION_,DEF.DEPLOYMENT_ID_asDEPLOYMENT_ID_fromACT_HI_PROCINSTRESDEF_DEF_PROConRES。PROC_DEF_ID_=DEF.ID_WHERERES.END_TIME_isnotNULLorderbyRES.ID_asc从这条SQL可以看出,这条查询本质上就是查询ACT_HI_PROCINST表。如下图所示:如果我们在查询的时候不限制流程是否完成,那么我们的查询方式如下:@Testvoidtest05(){Listlist=historyService.createHistoricProcessInstanceQuery().list();对于(HistoricProcessInstancehpi:list){logger.info("name:{},startTime:{},endTime:{}",hpi.getName(),hpi.getStartTime(),hpi.getEndTime());}}对应的查询SQL如下:SELECTRES.*,DEF.KEY_asPROC_DEF_KEY_,DEF.NAME_asPROC_DEF_NAME_,DEF.VERSION_asPROC_DEF_VERSION_,DEF.DEPLOYMENT_ID_asDEPLOYMENT_ID_fromACT_HI_PROCINSTRESleftouterjoinACT_RE_PROCDEFDEFonRES.PROC_DEF_ID_=DEF.ID_orderbyRES.ID_asc相比前面的SQL少了后面SQL中的WHERERES.END_TIME_isnotNULL条件,也就是说判断一个进程是否执行,要看它的END_TIME_是否为空,如果不为空,说明流程已经执行完毕,如果为空,说明流程还在执行中。2.历史任务查询我们刚才查询的是历史进程。接下来我们看一下历史任务,即查询某个进程中已经执行过的Task信息。下面的意思是查询所有历史流程任务:@Testvoidtest06(){Listlist=historyService.createHistoricTaskInstanceQuery().list();for(HistoricTaskInstancehti:list){logger.info("name:{},assignee:{},createTime:{},endTime:{}",hti.getName(),hti.getAssignee(),hti.getCreateTime(),hti.getEndTime());}}该查询对应的SQL如下:SELECTRES.*fromACT_HI_TASKINSTRESorderbyRES.ID_asc可以看出,历史任务表为ACT_HI_TASKINST,如下图:当然还有很多其他的这里的玩法,比如查询某个进程执行过的历史任务,如下:@Testvoidtest07(){ListinstanceList=historyService.createHistoricProcessInstanceQuery().list();for(HistoricProcessInstancehpi:instanceList){Listlist=historyService.createHistoricTaskInstanceQuery().processInstanceId(hpi.getId()).finished().list();对于(HistoricTaskInstancehti:列表){logger.info(“名称:{},分配ee:{},createTime:{},endTime:{}",hti.getName(),hti.getAssignee(),hti.getCreateTime(),hti.getEndTime());}}}查询历史任务在此SQL如下:SELECTRES.*fromACT_HI_TASKINSTRESWHERERES.PROC_INST_ID_=?andRES.END_TIME_isnotnullorderbyRES.ID_asc可以看到和前面相比多了两个条件:流程实例IDprocessendtime从这里也可以看出,这个finish方法的执行逻辑和我们前面说的一样3.历史活动查询历史任务就是各种Task,历史活动包含的内容比较多,比如start/end节点,连接等信息都算是活动,松哥在上一篇文章中已经给大家介绍过了,查询代码如下:@Testvoidtest08(){Listlist=historyService.createHistoricActivityInstanceQuery().list();for(HistoricActivityInstancehai:list){logger.info("name:{},startTime:{},assignee:{},type:{}",hai.getActivityName(),hai.getStartTime(),hai.getAssignee(),hai.getActivityType());}}该查询对应的SQL如下:SELECTRES.*fromACT_HI_ACTINSTRESorderbyRES.ID_asc可以看到ACT_HI_ACTINST表中存储了历史活动信息。4、历史变量查询查询进程执行的历史变量,如下:@Testvoidtest09(){HistoricProcessInstancepi=historyService.createHistoricProcessInstanceQuery().singleResult();Listlist=historyService.createHistoricVariableInstanceQuery().processInstanceId(pi.getId()).list();for(HistoricVariableInstancehvi:list){logger.info("name:{},type:{},value:{}",hvi.getVariableName(),hvi.getVariableTypeName(),hvi.getValue());}}该查询对应的SQL如下:SELECTRES.*fromACT_HI_VARINSTRESWHERERES.PROC_INST_ID_=?orderbyRES.ID_asc可以看到ACT_HI_VARINST表中存放了进程的历史变量信息。5、历史日志查询有些朋友看到日志这个词可能会觉得奇怪,嗯?还有流程执行的日志吗?从来没有听说过!历史日志查询其实是前者的高手,其用法如下:@Testvoidtest10(){HistoricProcessInstancepi=historyService.createHistoricProcessInstanceQuery().singleResult();ProcessInstanceHistoryLoghistoryLog=historyService.createProcessInstanceHistoryLogQuery(pi.getId())//包含历史活动.includeActivities()//包含历史任务.includeTasks()//包含历史变量.includeVariables().singleResult();logger.info("id:{},startTime:{},endTime:{}",historyLog.getId(),historyLog.getStartTime(),historyLog.getEndTime());ListhistoricData=historyLog.getHistoricData();for(HistoricDatadata:historicData){if(datainstanceofHistoricActivityInstance){HistoricActivityInstancehai=(HistoricActivityInstance)数据;logger.info("name:{},type:{}",hai.getActivityName(),hai.getActivityType());}if(datainstanceofHistoricTaskInstance){HistoricTaskInstancehti=(HistoricTaskInstance)数据;logger.info("name:{},assignee:{}",hti.getName(),hti.getAssignee());}if(datainstanceofHistoricVariableInstance){HistoricVariableInstancehvi=(HistoricVariableInstance)数据;logger.info("name:{},type:{},value:{}",hvi.getVariableName(),hvi.getVariableTypeName(),hvi.getValue());}}}在此,首先是查询基本流程日志信息,本质上是查询历史流程实例信息。对应的SQL如下:selectRES.*,DEF.KEY_asPROC_DEF_KEY_,DEF.NAME_asPROC_DEF_NAME_,DEF。VERSION_作为PROC_DEF_VERSION_,DEF.DEPLOYMENT_ID_作为DEPLOYMENT_ID_来自ACT_HI_PROCINSTRES左外连接ACT_RE_PROCDEFDEFonRES.PROC_DEF_ID_=DEF.ID_wherePROC_INST_ID_=?接下来我写了三个include,每个include对应一条ASQL语句:如下:SELECTRES.*fromACT_HI_ACTINSTRESWHERERES.PROC_INST_ID_=?按RES.ID_ascinclud排序eTasks对应的SQL如下:SELECTRES.*fromACT_HI_TASKINSTRESWHERERES.PROC_INST_ID_=?查询完成后调用getHistoricData方法查看新增数据。List集合中存储的HistoricData也分为不同的类型:includeActivities方法对应最终查询到的类型是HistoricActivityInstance,includeTasks方法对应最终查询到的类型是HistoricTaskInstance。includeVariables方法对应最终查询到的类型是HistoricVariableInstance。遍历的时候,使用类型判断,检查是哪个变量类型。综上所述,这个历史日志查询其实是个高手。6.历史权限查询这个用来查询进程或者任务的处理器,比如查询进程的处理器,方法如下:@Testvoidtest11(){HistoricProcessInstancepi=historyService.createHistoricProcessInstanceQuery().singleResult();Listlinks=historyService.getHistoricIdentityLinksForProcessInstance(pi.getId());对于(HistoricIdentityLink链接:链接){logger.info("userId:{}",link.getUserId());}}这是查询过程对应的处理器,对应的SQL如下:select*fromACT_HI_IDENTITYLINKwherePROC_INST_ID_=?如果要查询任务的处理者,对应的方法如下:@Testvoidtest12(){StringtaskName="提交请假申请";HistoricTaskInstancehti=historyService.createHistoricTaskInstanceQuery()。任务名(任务名).singleResult();Listlinks=historyService.getHistoricIdentityLinksForTask(hti.getId());for(HistoricIdentityLinklink:links){logger.info("{}任务由{}"处理,taskName,link.getUserId());}}该查询对应的SQL如下:select*fromACT_HI_IDENTITYLINKwhereTASK_ID_=?和上一个相比,其实多了一个查询条件TASK_ID_7。自定义查询SQL类似于上面提到的许多查询。当我们了解了每个历史查询的API操作的是哪张数据表,我们就会发现,历史数据查询,还可以自定义SQL。和朋友一起看一个例子,比如查询某个进程执行过的历史任务:@Testvoidtest13(){for(HistoricProcessInstancehpi:instanceList){Listlist=historyService.createNativeHistoricTaskInstanceQuery().sql("SELECTRES.*fromACT_HI_TASKINSTRESWHERERES.PROC_INST_ID_=#{pid}andRES.END_TIME_isnotnullorderbyRES.ID_asc").parameter("pid",hpi.getId()).list();for(HistoricTaskInstancehti:list){logger.info("name:{},assignee:{},createTime:{},endTime:{}",hti.getName(),hti.getAssignee(),hti.getCreateTime(),hti.getEndTime());}}}flowable底层是MyBatis,所有参数在SQL中的传递形式与MyBatis一致。8.历史数据记录级别Flowable需要记录哪些历史数据。有一个日志级别来描述这件事。默认有四个级别:无:这意味着不存储任何历史信息。好处是流程执行的时候效率会比较快。缺点是进程执行后,看不到已经执行过的进程信息。活动:这将存储所有流程实例和活动实例,并且在流程实例结束时,顶级流程实例变量的最新值将被复制到历史变量实例中,不存储任何细节。Audit:在Activity的基础上,还会存储历史细节,包括权限信息。默认日志记录级别是次要的。Full:这个是基于Audit的,也存储变量变化信息,会记录大量的数据,也会拖慢流程执行。总共有四个级别。在SpringBoot项目中,如果我们要配置日志级别,其实是很方便的。我们可以直接在application.properties中配置,如下:flowable.history-level=none配置加上这个配置,我们随便启动一个进程,然后查询ACT_HI_系列的表,发现都是空的,没有数据。如果我们将历史日志的级别改为activity,那么会记录流程信息和activity信息,但是没有执行过的Task(ACT_HI_TASKINST)等信息,包括流程参与者的信息(ACT_HI_IDENTITYLINK)等被记录下来。如果我们将历史日志的级别改为audit,就会记录上述类型的日志。但是ACT_HI_DETAIL表还是空的,不会详细记录一个流程变量的变化过程。如果我们将日志记录级别更改为完整,则会记录更多信息。流程变量的详细信息记录在ACT_HI_DETAIL表中。整个过程我就不给小伙伴们演示了,大家可以自己试试。好了,关于历史资料的查询,宋哥和小伙伴们聊了这么多~下篇继续~