Activiti 是一个针对企业用户、开发人员 、系统管理员的轻量级工作流业务管理平台(Business Process Management, 简称BPM),其核心是使用 Java 开发的快速 、 稳定的 BPMN2.0 流程引擎 。Activiti是开源的,并基于Apache许可发行。Activiti可以在任何Java应用程序、服务器、集群或云中运行,它可以与 Spring 完美集成。
Activiti Github地址:https://github.com/Activiti/Activiti
工作流的生命周期
在介绍Activiti的表结构之前,非常有必要先对工作流的生命周期有一定的了解。因为在对业务流程进行设计与运行的过程中,都在体现着工作流的生命周期,并且,Activiti的表结构中有一部分也是基于生命周期设计的。
一个完整的工作流生命周期会经过以下五个阶段:定义、发布、执行、监控、优化。
- 定义:业务需求人员收集业务需求,然后交由开发人员加工转化为计算机可以识别的流程定义
- 发布:开发人员打包各种资源,然后在系统管理平台中发布流程定义(包括流程定义文件 、自定义表单 、 任务监听类等资源 )
- 执行:流程引擎按照事先定义好的流程,以任务驱动的方式予以执行
- 监控:监控依赖执行阶段 。业务人员在办理任务的同时,引擎会收集每个任务的办理结果,然后根据结果做出处理
- 优化:对整个流程的运行结果进行分析,在此基础上进一步改进,并再次开始一个新的周期
Activiti表结构
Activiti(6.0.0)总共包含28张数据表,所有的表名默认以"ACT_"开头
Activiti数据表按类别可以分为五大类:
数据表分类 | 描述 |
---|---|
ACT_GE_* | 通用数据表(2张) |
ACT_HI_* | 历史数据表(8张) |
ACT_ID_* | 身份数据表(4张) |
ACT_RE_* | 仓库数据表(3张) |
ACT_RU_* | 运行时数据表(9张) |
上述总共26张表,剩余两张表:
数据表分类 | 描述 |
---|---|
ACT_EVT_LOG | 事件日志数据表 |
ACT_PROCDEF_INFO | 流程定义动态改变信息表 |
一、通用数据表
数据表分类 | 描述 |
---|---|
ACT_GE_PROPERTY | 属性表 |
ACT_GE_BYTEARRAY | 资源表 |
1. ACT_GE_PROPERTY
该表为属性表,保存流程引擎的KV键值属性,用于存储流程引擎的级别信息,包括数据库历史操作和数据库版本等,在启动流程引擎时创建。上图中底下的三条记录是创建数据库时自动插入的,插入语句写在建表的 sql 语句中。
该表的字段说明:
cfg.execution-related-entities-cout
:流程执行相关的实例计数器功能,默认是关闭的,开启后在 ACT_RU_EXECUTION 表的后几个带有 *COUNT 的字段会显示计数信息,可以通过配置 activiti.cfg.xml 添加以下语句来开启配置。
<property name="enableExecutionRelationshipCounts" value="true"/>
next.dbid
:记录数据库的主键ID,每对数据库进行一次修改(如部署流程定义文件)该值就会发生变化:next.dbid值+步长,默认步长idBlockSize = 2500。其中的 VALUE_ 和 REV_ 初始值为1,上图中表示我总共进行了59次修改:1+59*2500=147501schema.history
:表示对Activiti整个数据库的操作历史,这里是 create,表示目前仅做过创建表的操作,若整个数据库是从5.x的版本进行升级的,那么该值就会是update的值,同时 REV_ 的值会+1schema.history
:表示Activiti的数据库的版本
通过 ManagementService 的 getProperties 方法可以获取属性值
/**
* 获取属性表中的属性信息
*/
public static void getProperties(){
ManagementService managementService = processEngine.getManagementService();
Map<String, String> properties = managementService.getProperties();
for(String str: properties.keySet()){
logger.info(str + ":" + properties.get(str));
}
}
2. ACT_GE_BYTEARRAY
该表为资源表,用于保存Activiti运行过程中的资源(如:流程定义文件bpmn和对应的png图片,需要持久化保存的序列化对象,手工添加的资源信息)
关键字段说明:
REV_
:该字段为版本号,本质是一个乐观锁,若我们对资源文件进行过修改操作,该值+1;DEPLOYMENT_ID_
:流程资源文件对应的流程部署ID;BYTES_
:流程定义文件一般都是以大对象进行保存,类型为BLOB;点击该字段的值,若为PNG的图片信息,我们将显示方式由文本改为图片,会在下方显示流程图片;GENERATED_
:表示该资源是手动添加的还是自动生成的,0为用户添加,1为引擎生成;若我们在部署资源文件的时候只添加了bpmn文件,流程引擎会为我们自动生成 png的图片
除了以资源文件添加的方式,也可以通过手工方式添加资源信息,我们以插入一个字符串为例:
public static void testByteArrayInsert(){
ManagementService managementService = processEngine.getManagementService();
managementService.executeCommand(new Command<Object>() {
@Override
public Object execute(CommandContext commandContext) {
ByteArrayEntityImpl entity = new ByteArrayEntityImpl();
entity.setId("10004");
entity.setName("testSource");
entity.setBytes("testSource".getBytes());
commandContext.getByteArrayEntityManager().insert(entity);
// commandContext.getByteArrayEntityManager().deleteByteArrayById("10004");
return null;
}
});
}
二、流程定义存储表
数据表分类 | 描述 |
---|---|
ACT_RE_DEPLOYMENT | 流程部署表 |
ACT_RE_PROCDEF | 流程定义表 |
ACT_RE_MODEL | 流程模型表 |
1. ACT_RE_DEPLOYMENT
该表为流程部署表,用于记录流程的部署情况(流程部署的名称、类别、key标识、租户ID、部署时间等)
关键字段说明:
CATEGORY_
:用于标记该流程所属的流程类型KEY_
:流程标识TENANT_ID_
:该流程所属的租户,不同租户对应的流程为不同的版本
想要对上表中的字段有更清晰地认识,可以操纵下面这段代码:
/**
* 部署流程定义文件
*/
private static void deploy() {
Deployment deployment = processEngine.getRepositoryService()
.createDeployment()
.addClasspathResource("diagrams/MyProcess.bpmn")
.addClasspathResource("diagrams/MyProcess.png")
.name("FirstApprove流程")
.category("测试流程")
.tenantId("A")
.key("simpleForm")
.activateProcessDefinitionsOn(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS")
.parse("2020-04-15 09:46:00:545"))
.deploy();
System.out.println("流程部署ID:"+deployment.getId());
System.out.println("流程部署Name:"+deployment.getName());
}
1)当我们部署了一个流程的时候,默认是激活状态的,若我们设置了 activateProcessDefinitionsOn则表示流程未激活,需要以手工方式进行激活。2)上述代码执行后,相应的有三张表发生了变化,ACT_GE_BYTEARRAY 表中增加了两条资源记录,ACT_RE_DEPLOYMENT 表中增加了一条部署记录,ACT_RE_PROCDEF 表中增加了一条流程定义信息记录。
2. ACT_RE_PROCDEF
该表为流程定义信息表,用于记录部署的流程定义信息
关键字段说明:
-
REV_
:表示的是乐观锁(对流程的修改操作后,该字段值加1,如对流程定义的暂定和激活操作)
-
CATEGORY_
:流程定义类别,表示的是流程定义文件(xml格式)中的targetNamespace的属性值
-
NAME_
:流程定义文件的 name 属性值 -
KEY_
:流程定义文件的ID值,我们一般以 KEY 值作为流程定义的唯一表标识,并且通过 KEY 值来启动流程定义文件 -
VERSION_
:流程定义的版本号,只要流程定义的ID值不变,每进行一次流程发布,该值加1 -
DESCRIPTION
和HAS_START_FORM_KEY_
对应流程定义文件:
-
SUSPENSION_STATE_
:流程暂停状态,1为激活状态,2为暂停挂起状态。
3. ACT_RE_MODEL
模型信息表(用于WEB设计器),在5.x的版本用于WEB设计器,一些json数据会存在此表中,这里不做过多说明。
三、运行时数据表
数据表分类 | 描述 |
---|---|
ACT_RU_EXECUTION | 运行时流程实例执行表 |
ACT_RU_TASK | 运行时任务表 |
ACT_RU_IDENTITYLINK | 运行时参与者信息表 |
ACT_RU_VARIABLE | 运行时变量表 |
ACT_RU_EVENT_SUBSCR | 运行时事件订阅表 |
ACT_RU_JOB | 运行时作业表 |
ACT_RU_TIMER_JOB | 运行时定时器表 |
ACT_RU_SUSPENDED_JOB | 运行时暂停作业表 |
ACT_RU_DEADLETTER_JOB | 运行时死信表 |
1. ACT_RU_EXECTION
该表记录运行时的流程实例与分支的执行信息
该运行时执行表为启动下面的流程图后的结果,我们启动该流程,首先进入到callActivity(调用式子流程),该子流程会调用第二幅图中的流程,因此运行时的执行表会有这么多条记录(包括两条主干线,以及其他的节点记录)。
关键字段说明:
BUSINESS_KEY_
:为业务标识,主流程才会拥有业务主键,可以在开启流程实例的时候注入,该业务标识只有在主线流程中可以查看到。
PARENT_ID_
:父执行ID,对应的是上一执行信息的ID号SUPER_EXEC_
:调用式子流程的父执行ID(一般为CallActiviti所对应的ID)IS_ACTIVE_
:是否活动流程实例,0为非活动,1为活动(一个分支节点完成,则该分支的IS_ACTIVE会变为0)- IS_CONCURRENT_:是否并发,0为非并发,1为并发(这里的并发并非并行执行的概念)
IS_SCOPE
:是否全局流程执行,0非,1是- IS_EVENT_SCOPE_:是否激活状态,没有使用到事件的情况下,一般都为0
- IS_MI_ROOT_:是否多实例根执行流
SUSPENSION_STATE_
:挂起状态,1正常,2挂起- CACHED_ENT_STATE_:缓存结束状态
- LOCK_TIME_:锁定时间,锁定时间是不被并发操作的
剩下的几个带有 _COUNT_的字段为其他几张运行时表的记录数,需要配置activiti.cfg.xml的
<property name=“enableExecutionRelationshipCounts” value=“true”/>计数器功能,才生效
2. ACT_RU_TASK
该表记录运行时的 userTask任务信息。
关键字段说明:
- PARENT_TASK_ID_:父节点任务ID,由父节点间接启动
- OWNER_:拥有人,一般情况下为空,只有在委托时才有值
- DELEGATION_:委托状态,PENDING委托中,RESOLVED已处理,无委托则为空
PRIORITY_
:优先级别,默认为50(Main Config中可配置)DUE_DATE_
:过期时间(Main Config中可配置)CATEGORY_
:类别(Main Config中可配置)SUSPENSION_STATE_
:挂起状态,1正常,2挂起CLAIM_TIME_
:该任务节点的获取时间
3. ACT_RU_VARIABLE
记录运行时的变量信息
关键字段说明:
TYPE_
:类型integer,String,double,json,null等BYTEARRAY_ID_
:资源信息ID,若该变量需要用到资源表中的信息,则对应的是资源表中的ID值TEXT2_
:存储持久化对象的对象ID
4. ACT_RU_IDENTITYLINK
记录运行时相关的用户信息
关键字段说明:
TYPE_
:类型assignee,candidate,owner,starter,participant
设置用户类别的方式为:
@Test
public void query(){
TaskService taskService = processEngine.getTaskService();
Task task = taskService.createTaskQuery().processDefinitionKey("simpleProcess").singleResult();
// taskService.setOwner(task.getId(), "李四");
taskService.addUserIdentityLink(task.getId(), "赵六", IdentityLinkType.STARTER);
}
5. ACT_RU_EVENT_SUBSCR
事件订阅表,记录与消息事件定义相关的信息
事件订阅表可以通过部署并启动
包含消息事件的流程定义来测试
(案例1)消息开始事件
流程定义文件(my-process-message.bpmn20.xml)如下:
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:activiti="http://activiti.org/bpmn"
xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC"
xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" typeLanguage="http://www.w3.org/2001/XMLSchema"
expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.activiti.org/test">
<message id="messageStart" name="my-message"></message>
<process id="myProcessMessage">
<startEvent id="start" >
<messageEventDefinition messageRef="messageStart" />
</startEvent>
<sequenceFlow id="flow1" sourceRef="start" targetRef="someTask" />
<userTask id="someTask" name="Activiti is awesome!" />
<sequenceFlow id="flow2" sourceRef="someTask" targetRef="end" />
<endEvent id="end" />
</process>
</definitions>
(案例2)中间捕获消息事件定义
流程定义文件(my-process-message-received.bpmn20.xml)如下:
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:activiti="http://activiti.org/bpmn"
xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC"
xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" typeLanguage="http://www.w3.org/2001/XMLSchema"
expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.activiti.org/test">
<message id="messageStart" name="my-message"></message>
<process id="myProcessCatchMessage">
<startEvent id="start" />
<sequenceFlow id="flow1" sourceRef="start" targetRef="message-received" />
<intermediateCatchEvent id = "message-received">
<messageEventDefinition messageRef="messageStart" />
</intermediateCatchEvent>
<!--<userTask id="someTask" name="Activiti is awesome!" />-->
<sequenceFlow id="flow2" sourceRef="message-received" targetRef="end" />
<endEvent id="end" />
</process>
</definitions>
关键字段说明:
EVENT_TYPE_
:事件订阅类型,message,signalEVENT_NAME_
:事件订阅名称ACTIVITY_ID_
:流程定义节点IDCONFIGURATION_
:配置信息(若在开始事件中配置了消息事件定义,则该配置信息即流程定义ID)CREATED_
:创建时间,时间戳形式
6. ACT_RU_JOB
运行时作业表,这一类的总共有四张表:
- ACT_RU_JOB:记录异步任务的数据
- ACT_RU_TIMER_JOB:记录定时事件的数据
- ACT_RU_SUSPENDED_JOB:记录暂停的的数据,比如流程中有一个定时任务,如果把这个任务停止工作了,这个任务会在ACT_RU_SUSPENDED_JOB中写入数据
- ACT_RU_DEADLETTER_JOB:无法执行工作表的数据, 如果一个任务执行了很多次,都无法执行,那么这个任务会写到该表中
备注ps:
- ACT_RU_JOB 和 ACT_RU_TIMER_JOB 两张表的表结构为一模一样的
- ACT_RU_JOB 比 ACT_RU_DEADLETTER_JOB 多了三个字段LOCK_EXP_TIME_、LOCK_OWNER_和 RETRIES
- ACT_RU_JOB 比 ACT_RU_SUSPENDED_JOB 多了两个字段LOCK_EXP_TIME_、LOCK_OWNER_
(案例)定时开始事件
启动一个定时事件事件流程(my-process-job.bpmn20.xml):
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:activiti="http://activiti.org/bpmn"
xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC"
xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" typeLanguage="http://www.w3.org/2001/XMLSchema"
expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.activiti.org/test">
<process id="my-process">
<startEvent id="start">
<timerEventDefinition> <!-- 配置定时器 -->
<timeCycle>R5/PT10S</timeCycle> <!--循环执行5次,每10秒执行一次-->
</timerEventDefinition>
</startEvent>
<sequenceFlow id="flow1" sourceRef="start" targetRef="someTask" />
<userTask id="someTask" name="Activiti is awesome!" />
<sequenceFlow id="flow2" sourceRef="someTask" targetRef="end" />
<endEvent id="end" />
</process>
</definitions>
关键字段说明:
LOCK_OWNER_
:锁定节点EXCLUSIVE_
:是否唯一RETRIES_
:重试次数3EXCEPTION_STACK_ID_
:异常堆栈(资源表ID)EXCEPTION_MSG_
:异常信息REPEAT_
:重复表达式,如R5/PT10SHANDLER_TYPE_
:处理器类型,如 timer-start-eventHANDLER_CFG_
:处理器配置,如 {“activityId”:“start”}表示配置的节点为 start 节点
四、身份数据表
数据表分类 | 描述 |
---|---|
ACT_ID_USER | 用户基本信息表 |
ACT_ID_GROUP | 用户组表 |
ACT_ID_MEMBERSHIP | 用户与组关系表 |
ACT_ID_INFO | 用户扩展信息表 |
这一类表是五大类中最简单,最容易理解的,但一般在项目开发中,我们通常会使用自己的用户表。
Activiti中对用户和用户组的支持自带有POJO对象,并支持序列化。如下面表示 ACT_ID_USER 表对应的POJO类
1. ACT_ID_USER
该表存放用户的基本信息,包括用户名、电子邮箱、密码、照片等
REV
:仍然表示的是乐观锁,表示对该用户进行修改的次数。
public static void setUser() throws Exception {
User user = identityService.newUser("004");
user.setFirstName("刘");
user.setLastName("启明");
user.setEmail("[email protected]");
user.setPassword("123456");
identityService.saveUser(user);
//为某个用户设置照片
InputStream imInputStream = IdentityServiceTest.class.getClassLoader()
.getResourceAsStream("diagrams/photo.jpeg");
byte[] data = toByteArray(imInputStream);
String mimeType = "photo";
Picture picture = new Picture(data, mimeType);
identityService.setUserPicture("001" ,picture);
System.out.println("设置用户成功");
}
2. ACT_ID_GROUP
用户组表存放用户的群组信息,包括用户组名、组类型。
private static void setGroup(){
Group userGroup = identityService.newGroup("01");
userGroup.setName("用户组");
userGroup.setType("用户");
identityService.saveGroup(userGroup);
System.out.println("设置组成功");
}
当然,上面的 ID_值,不一定要是数字,可以是字符串类型。
3.ACT_ID_MEMBERSHIP
用户关系表存放用户隶属的表,通过 IdentityServiceImpl 的 createMembership(String userId, String groupId) 方法可以直接创建用户组关系
private static void setMembership(){
identityService.createMembership("001", "01");
identityService.createMembership("003", "01");
identityService.createMembership("004", "01");
identityService.createMembership("002", "02");
identityService.createMembership("003", "03");
identityService.createMembership("004", "04");
System.out.println("设置用户关系成功");
}
同样的,这里的 USER_ID_ 和 GROUP_ID_ 只要是字符串类型就可以。
4. ACT_ID_INFO
用于存放除基本信息外的的扩展信息,如年龄、地址、联系方式等
private static void setInfo(){
identityService.setUserInfo("001", "age", "18");
identityService.setUserInfo("001", "address", "BeiJing");
}
关键字段说明:
TYPE_
:信息类型,可以设置用户账号(account)、用户信息(userinfo)和NULL三种值。PASSWORD_
:用户账号的密码字段,当前的Activiti版本并没有使用该字段。PARENT_ID_
:该信息的父信息ID,如果一条数据设置了父信息ID,则表示该数据是用户账号(信息)的明细数据,例如一个账号有激活日期,那么激活日期就是该账号的明细数据,此处使用了自关联来实现。
五、历史数据表
数据表分类 | 描述 |
---|---|
ACT_HI_ACTINST | 历史节点信息表 |
ACT_HI_ATTACHMENT | 历史附件表 |
ACT_HI_COMMENT | 历史评论表 |
ACT_HI_DETAIL | 历史详细变更表 |
ACT_HI_IDENTITYLINK | 历史参与者表 |
ACT_HI_PROCINST | 历史流程实例表 |
ACT_HI_TASKINST | 历史任务表 |
ACT_HI_VARINST | 历史变量表 |
1. ACT_HI_PROCINST
历史流程实例表,该表记录流程引擎启动的流程实例。只要有一条实例启动,无论该实例是否完成,都会在该表中插入一条记录。
关键字段说明:
-
DURATION_
:记录流程开始到结束的时间,单位为毫秒 -
SUPER_PROCESS_INSTANCE_ID_
:调用式子流程中的父流程的ID值
2. ACT_HI_ACTINST
历史节点信息表,该表记录流程运行过程中的每个节点信息,包括开始节点、结束节点、用户任务、服务任务、网关等
关键字段说明:
-
TASK_ID_
:只有用户任务才有TASK_ID值 -
CALL_PROC_INST_ID_
:针对callActivity调用式子流程中的被调用的流程实例ID值
3. ACT_HI_TASKINST
用户任务实例表,记录历史的用户任务信息
关键字段说明:
PARENT_TASK_ID_
:父节点调用的任务IDCLAIM_TIME_
:该节点的获取时间
六、剩余两张表
1. ACT_EVT_LOG
事件日志表,该表不可修改,只能写入和删除
关键字段说明:
LOG_NR_
:自增主键(Activiti表中唯一有自增主键的表)TYPE_
:类型TIME_STAMP_
:时间戳,写入事件的时间点USER_ID_
:用户DATA_
:内容数据,json序列化存到数据库中,内容非常大(为该表中的最核心的数据,官方建议不要存储到mysql中而是存到MongoDB或者elisticsearch中,自动完成对json数据的解析)LOCK_OWNER_
:锁定节点LOCK_TIME_
:锁定时间IS_PROCESSED_
:是否正在执行