乐趣区

SpringBoot-Activiti6系列教程六Execution说明

在 activiti 中有几个概念经常用,但文档也没有讲的很清楚,如果不理解,有可能会误用。本文就详细探讨以下几个概念

  • deployment(部署)
  • instance(实例)
  • execution(执行)
  • task(任务)

deployment(部署)

我们在前面章节讲过,bpmn 流程模型文件需要通过 RepositoryService 进行发布,相同名称的流程模型重新部署后会升一个版本,但并不影响之前的流程,还未结束的流程还是会按照发起时候的版本走。因此 deployment 就表示一次资源的部署,资源类型不一定是 bpmn 文件,可以是任意文件。上传的资源元信息保存在 ACT_RE_DEPLOYMENT 中,资源内容以二进制形式保存在表 ACT_GE_BYTEARRAY 中,两张表通过 DEPLOYMENT_ID 字段关联

instance(流程实例)

一个流程每发起一次就是一个实例,实例数也是流程统计的重要数据,一个流程如果有 10000 个实例就表示这个流程已经被发起了 10000 次,流程实例在 activiti 中是没有实体表存储的,在流程发起时系统分配一个实例 id(PROC_INST_ID_),在整个流程的流转中,该实例号都保持不变,因此可以根据 PROC_INST_ID_查询整个流程运行时的数据。当调用 api 发起流程时,应用程序应需要把 PROC_INST_ID_ 保存到自身的业务表中,后续对流程的管理和追踪都需要 PROC_INST_ID_,如要查询指定流程目前审批人信息,执行以下 api:

TaskService taskService = ProcessEngines.getDefaultProcessEngine().getTaskService();
List<Task> tasks = taskService.createTaskQuery()
        .processInstanceId(instanceId)
        .list();

如果要追踪流程运行时信息,可以以 PROC_INST_ID_作为过滤条件,你可以根据 PROC_INST_ID_查询以下表的信息

mysql> SELECT table_name,column_name FROM information_schema.COLUMNS where COLUMN_NAME='PROC_INST_ID_';
+-----------------------+---------------+
| TABLE_NAME            | COLUMN_NAME   |
+-----------------------+---------------+
| ACT_EVT_LOG           | PROC_INST_ID_ |
| ACT_FO_SUBMITTED_FORM | PROC_INST_ID_ |
| ACT_HI_ACTINST        | PROC_INST_ID_ |
| ACT_HI_ATTACHMENT     | PROC_INST_ID_ |
| ACT_HI_COMMENT        | PROC_INST_ID_ |
| ACT_HI_DETAIL         | PROC_INST_ID_ |
| ACT_HI_IDENTITYLINK   | PROC_INST_ID_ |
| ACT_HI_PROCINST       | PROC_INST_ID_ |
| ACT_HI_TASKINST       | PROC_INST_ID_ |
| ACT_HI_VARINST        | PROC_INST_ID_ |
| ACT_RU_EVENT_SUBSCR   | PROC_INST_ID_ |
| ACT_RU_EXECUTION      | PROC_INST_ID_ |
| ACT_RU_IDENTITYLINK   | PROC_INST_ID_ |
| ACT_RU_TASK           | PROC_INST_ID_ |
| ACT_RU_VARIABLE       | PROC_INST_ID_ |
+-----------------------+---------------+
15 rows in set (0.01 sec)

execution(执行)

execution 是比较难理解的一个概念,官方的解释是 Represent a 'path of execution' in a process instance,代表流程实例的执行路径,有点抽象,什么意思呢,当流程没有分之,start 到 end 就是一条直线,那么就只有一条执行路径,就是一个 execution,如果流程有分之,比如平行审批,那么到平行网关就会分出两条路径,这时候就会产生两个的 execution,新的 execution 通过PARENT_ID_ 与父 exection 进行关联,之所以要这么设计,我觉得是为了隔离不同执行路径间数据的隔离,比如在程序分之 A 上定义了一个变量,程序是不知道流程是否在分之上,在分之 B 上又重新定义了相同的变量,如果没有 execution 的概念就会导致变量被覆盖。

有一个比较特别的地方需要注意下,流程实例 (ProcessInstance) 也是一个 execution,实例 id(PROC_INST_ID_)就是流程实例在 execution 表上的 id,从源码上也可以看出:

下面我们通过两个具体例子来理解 execution

  • 简单无分之流程

以上就是一个简单的流程,流程发起后到 001 节点,001 审批后到 002,002 审批后流程结束,通过代码进行发起,保存实例 id,后续通过实例 id 进行观察

发起代码

public String start() {ProcessInstance instance = runtimeService.startProcessInstanceByKey("execution_simple");
    return instance.getId();}

发起后表信息

mysql> SELECT id_,PROC_INST_ID_,PARENT_ID_,PROC_DEF_ID_,ROOT_PROC_INST_ID_,ACT_ID_ FROM activiti.ACT_RU_EXECUTION where proc_inst_id_=77508;
+-------+---------------+------------+-------------------+--------------------+---------+
| id_   | PROC_INST_ID_ | PARENT_ID_ | PROC_DEF_ID_      | ROOT_PROC_INST_ID_ | ACT_ID_ |
+-------+---------------+------------+-------------------+--------------------+---------+
| 77508 | 77508         | NULL       | myProcess:2:77507 | 77508              | NULL    |
| 77509 | 77508         | 77508      | myProcess:2:77507 | 77508              | task1   |
+-------+---------------+------------+-------------------+--------------------+---------+
2 rows in set (0.01 sec)

mysql> select ID_,EXECUTION_ID_,PROC_INST_ID_,PROC_DEF_ID_,NAME_,TASK_DEF_KEY_ from ACT_RU_TASK WHERE PROC_INST_ID_=77508;
+-------+---------------+---------------+-------------------+-------+---------------+
| ID_   | EXECUTION_ID_ | PROC_INST_ID_ | PROC_DEF_ID_      | ASSIGNEE_ | TASK_DEF_KEY_ |
+-------+---------------+---------------+-------------------+-----------+---------------+
| 77512 | 77509         | 77508         | myProcess:2:77507 | 001       | task1         |
+-------+---------------+---------------+-------------------+-----------+---------------+
1 row in set (0.00 sec)

我们可以得到以下几个信息

  • PROC_INST_ID_为 77508,和第一条 exection 的 id 一样
  • 目前有产生两个 execution,77512 的 task 对应的 execution 为 77509
  • 77509 的父 execution 是 77508 也就是流程实例的 execution,ACT_ID_为 task1,也就是第一个节点,说明目前该 execution 目前停留在第一个节点

001 审批后表信息

mysql> SELECT id_,PROC_INST_ID_,PARENT_ID_,PROC_DEF_ID_,ROOT_PROC_INST_ID_,ACT_ID_ FROM activiti.ACT_RU_EXECUTION where proc_inst_id_=77508;
+-------+---------------+------------+-------------------+--------------------+---------+
| id_   | PROC_INST_ID_ | PARENT_ID_ | PROC_DEF_ID_      | ROOT_PROC_INST_ID_ | ACT_ID_ |
+-------+---------------+------------+-------------------+--------------------+---------+
| 77508 | 77508         | NULL       | myProcess:2:77507 | 77508              | NULL    |
| 77509 | 77508         | 77508      | myProcess:2:77507 | 77508              | task2   |
+-------+---------------+------------+-------------------+--------------------+---------+
2 rows in set (0.00 sec)

mysql> select ID_,EXECUTION_ID_,PROC_INST_ID_,PROC_DEF_ID_,ASSIGNEE_,TASK_DEF_KEY_ from ACT_RU_TASK WHERE PROC_INST_ID_=77508;
+-------+---------------+---------------+-------------------+-------+---------------+
| ID_   | EXECUTION_ID_ | PROC_INST_ID_ | PROC_DEF_ID_      | ASSIGNEE_ | TASK_DEF_KEY_ |
+-------+---------------+---------------+-------------------+-----------+---------------+
| 77515 | 77509         | 77508         | myProcess:2:77507 | 002       | task2         |
+-------+---------------+---------------+-------------------+-------------+---------------+
1 row in set (0.00 sec)

execution 信息不变,因为当前流程没有分之。

  • 平行审批流程

流程发起

mysql> SELECT id_,PROC_INST_ID_,PARENT_ID_,PROC_DEF_ID_,ROOT_PROC_INST_ID_,ACT_ID_ FROM activiti.ACT_RU_EXECUTION where proc_inst_id_=80011;
+-------+---------------+------------+-------------------------+--------------------+---------+
| id_   | PROC_INST_ID_ | PARENT_ID_ | PROC_DEF_ID_            | ROOT_PROC_INST_ID_ | ACT_ID_ |
+-------+---------------+------------+-------------------------+--------------------+---------+
| 80011 | 80011         | NULL       | parallelProcess:1:80009 | 80011              | NULL    |
| 80012 | 80011         | 80011      | parallelProcess:1:80009 | 80011              | task1   |
+-------+---------------+------------+-------------------------+--------------------+---------+
2 rows in set (0.00 sec)

mysql> mysql> select ID_,EXECUTION_ID_,PROC_INST_ID_,PROC_DEF_ID_,ASSIGNEE_,TASK_DEF_KEY_ from ACT_RU_TASK WHERE PROC_INST_ID_=80011;
+-------+---------------+---------------+-------------------------+-----------+---------------+
| ID_   | EXECUTION_ID_ | PROC_INST_ID_ | PROC_DEF_ID_            | ASSIGNEE_ | TASK_DEF_KEY_ |
+-------+---------------+---------------+-------------------------+-----------+---------------+
| 80015 | 80012         | 80011         | parallelProcess:1:80009 | 001       | task1         |
+-------+---------------+---------------+-------------------------+-----------+---------------+
1 row in set (0.00 sec)

001 审批后

mysql> SELECT id_,PROC_INST_ID_,PARENT_ID_,PROC_DEF_ID_,ROOT_PROC_INST_ID_,ACT_ID_ FROM activiti.ACT_RU_EXECUTION where proc_inst_idINST_ID_=80011;
+-------+---------------+------------+-------------------------+--------------------+---------+
| id_   | PROC_INST_ID_ | PARENT_ID_ | PROC_DEF_ID_            | ROOT_PROC_INST_ID_ | ACT_ID_ |
+-------+---------------+------------+-------------------------+--------------------+---------+
| 80011 | 80011         | NULL       | parallelProcess:1:80009 | 80011              | NULL    |
| 80012 | 80011         | 80011      | parallelProcess:1:80009 | 80011              | task3   |
| 80018 | 80011         | 80011      | parallelProcess:1:80009 | 80011              | task2   |
+-------+---------------+------------+-------------------------+--------------------+---------+
3 rows in set (0.00 sec)

mysql> select ID_,EXECUTION_ID_,PROC_INST_ID_,PROC_DEF_ID_,ASSIGNEE_,TASK_DEF_KEY_ from ACT_RU_TASK WHERE PROC_INST_ID_=80011;
+-------+---------------+---------------+-------------------------+-----------+---------------+
| ID_   | EXECUTION_ID_ | PROC_INST_ID_ | PROC_DEF_ID_            | ASSIGNEE_ | TASK_DEF_KEY_ |
+-------+---------------+---------------+-------------------------+-----------+---------------+
| 80020 | 80012         | 80011         | parallelProcess:1:80009 | 003       | task3         |
| 80023 | 80018         | 80011         | parallelProcess:1:80009 | 002       | task2         |
+-------+---------------+---------------+-------------------------+-----------+---------------+
2 rows in set (0.00 sec)

可以看出 80018 是新创建的 execution,task3 复用了原来的 exection 80012,两个 execution 的父 id 都是 80011,也就是流程实例 execution。

003 审批后

mysql> SELECT id_,PROC_INST_ID_,PARENT_ID_,PROC_DEF_ID_,ROOT_PROC_INST_ID_,ACT_ID_ FROM activiti.ACT_RU_EXECUTION where proc_inst_id_=80011;
+-------+---------------+------------+-------------------------+--------------------+------------------+
| id_   | PROC_INST_ID_ | PARENT_ID_ | PROC_DEF_ID_            | ROOT_PROC_INST_ID_ | ACT_ID_          |
+-------+---------------+------------+-------------------------+--------------------+------------------+
| 80011 | 80011         | NULL       | parallelProcess:1:80009 | 80011              | NULL             |
| 80012 | 80011         | 80011      | parallelProcess:1:80009 | 80011              | parallelgateway2 |
| 80018 | 80011         | 80011      | parallelProcess:1:80009 | 80011              | task2            |
+-------+---------------+------------+-------------------------+--------------------+------------------+
3 rows in set (0.00 sec)

mysql> select ID_,EXECUTION_ID_,PROC_INST_ID_,PROC_DEF_ID_,ASSIGNEE_,TASK_DEF_KEY_ from ACT_RU_TASK WHERE PROC_INST_ID_=80011;
+-------+---------------+---------------+-------------------------+-----------+---------------+
| ID_   | EXECUTION_ID_ | PROC_INST_ID_ | PROC_DEF_ID_            | ASSIGNEE_ | TASK_DEF_KEY_ |
+-------+---------------+---------------+-------------------------+-----------+---------------+
| 80023 | 80018         | 80011         | parallelProcess:1:80009 | 002       | task2         |
+-------+---------------+---------------+-------------------------+-----------+---------------+
1 row in set (0.00 sec)

80012 停留在 parallelgateway2 上等待 80018 结束,没有新的 execution 产生

002 审批后

mysql> SELECT id_,PROC_INST_ID_,PARENT_ID_,PROC_DEF_ID_,ROOT_PROC_INST_ID_,ACT_ID_ FROM activiti.ACT_RU_EXECUTION where proc_inst_id_=80011;
+-------+---------------+------------+-------------------------+--------------------+---------+
| id_   | PROC_INST_ID_ | PARENT_ID_ | PROC_DEF_ID_            | ROOT_PROC_INST_ID_ | ACT_ID_ |
+-------+---------------+------------+-------------------------+--------------------+---------+
| 80011 | 80011         | NULL       | parallelProcess:1:80009 | 80011              | NULL    |
| 80018 | 80011         | 80011      | parallelProcess:1:80009 | 80011              | task4   |
+-------+---------------+------------+-------------------------+--------------------+---------+
2 rows in set (0.00 sec)

mysql> select ID_,EXECUTION_ID_,PROC_INST_ID_,PROC_DEF_ID_,ASSIGNEE_,TASK_DEF_KEY_ from ACT_RU_TASK WHERE PROC_INST_ID_=80011;
+-------+---------------+---------------+-------------------------+-----------+---------------+
| ID_   | EXECUTION_ID_ | PROC_INST_ID_ | PROC_DEF_ID_            | ASSIGNEE_ | TASK_DEF_KEY_ |
+-------+---------------+---------------+-------------------------+-----------+---------------+
| 80028 | 80018         | 80011         | parallelProcess:1:80009 | 004       | task4         |
+-------+---------------+---------------+-------------------------+-----------+---------------+
1 row in set (0.00 sec)

复用了 80018 这个 execution

004 审批后流程结束。

通过以上例子可以看出

  • 如果没有分之,就只有两个 execution(另外一个是实例 execution)
  • 遇到分之节点会产生新的 execution,activiti 会复用之前的 execution,分之合并后保留其中一个 execution

实际上不只是分之,如果一个节点多个人审批,通过 multiInstanceLoopCharacteristics 指定,那么每次循环都会创建一个新的 execution,这个就留给读者自行去验证。

task(任务)

这里说的 task 指的是 usertask,usertask 就是一个人的待办,可以通过 activiti 提供的 api 可以查询指定人当前待办信息,当 activiti 遇到 usertask 节点就会进入等待状态,等待用户审批完成后继续执行。用户的待办任务存储在 ACT_RU_TASK 表上。

退出移动版