接上一篇文章,我们探究了execution的运行机制,activiti里变量的作用域就是通过execution实现,activiti里变量按作用域有以下几种
- 执行变量(variable)
- 本地变量(LocalVariable)
- 临时变量(TransVariable)
执行变量作用域在execution上,本地变量作用域在task上,临时变量不存储数据库,流程进入等待节点(比如UserTask)变量自动清除。
需要使用变量的是RuntimeService
和TaskService
,本文主要对这两个service中变量的使用做详细说明,避免在不了解变量使用范围的情况下,错误的使用变量,导致流程出错。
变量表结构
变量存储在表ACT_RU_VARIABLE
上,
mysql> describe ACT_RU_VARIABLE;
+---------------+---------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------------+---------------+------+-----+---------+-------+
| ID_ | varchar(64) | NO | PRI | NULL | |
| REV_ | int(11) | YES | | NULL | |
| TYPE_ | varchar(255) | NO | | NULL | |
| NAME_ | varchar(255) | NO | | NULL | |
| EXECUTION_ID_ | varchar(64) | YES | MUL | NULL | |
| PROC_INST_ID_ | varchar(64) | YES | MUL | NULL | |
| TASK_ID_ | varchar(64) | YES | MUL | NULL | |
| BYTEARRAY_ID_ | varchar(64) | YES | MUL | NULL | |
| DOUBLE_ | double | YES | | NULL | |
| LONG_ | bigint(20) | YES | | NULL | |
| TEXT_ | varchar(4000) | YES | | NULL | |
| TEXT2_ | varchar(4000) | YES | | NULL | |
+---------------+---------------+------+-----+---------+-------+
12 rows in set (0.00 sec)
NAME_
是变量名称,TYPE_
是变量类型,根据TYPE_
类型会将数据保存到不同的字段里,如TYPE_为string则保存到TEXT_
或者TEXT_2
中,如果是二进制数据,数据存储到表ACT_GE_BYTEARRAY
中,并将id存储到BYTEARRAY_ID_
字段上。EXECUTION_ID_、PROC_INST_ID_、TASK_ID_
控制变量的作用域,如果TASK_ID_为null则为执行变量,如果不为null则为本地变量。
TaskService变量
TaskService
有两个设置变量相关的API(实际有四个,但另外两个功能类似),定义如下:
/**
* set variable on a task. If the variable is not already existing, it will be created in the most outer scope. This means the process instance in case this task is related to an execution.
*/
void setVariable(String taskId, String variableName, Object value);
/**
* set variable on a task. If the variable is not already existing, it will be created in the task.
*/
void setVariableLocal(String taskId, String variableName, Object value);
setVariable设置的是全局变量,也就是instance的变量,设置完后流程全局可见,setVariableLocal设置的是本地变量,设置完后只有该task可见。
执行如下示例代码:
taskService.setVariable(task.getId(), "variable", "this is Variable");
taskService.setVariableLocal(task.getId(), "localVariable", "this is Local Variable");
mysql> select * from activiti.ACT_RU_VARIABLE t where t.proc_inst_id_=100001 and name_='variable' or name_='localVariable'\G;
*************************** 1. row ***************************
ID_: 100024
REV_: 1
TYPE_: string
NAME_: variable
EXECUTION_ID_: 100001
PROC_INST_ID_: 100001
TASK_ID_: NULL
BYTEARRAY_ID_: NULL
DOUBLE_: NULL
LONG_: NULL
TEXT_: this is Variable
TEXT2_: NULL
*************************** 2. row ***************************
ID_: 100025
REV_: 1
TYPE_: string
NAME_: localVariable
EXECUTION_ID_: 100008
PROC_INST_ID_: 100001
TASK_ID_: 100015
BYTEARRAY_ID_: NULL
DOUBLE_: NULL
LONG_: NULL
TEXT_: this is Local Variable
TEXT2_: NULL
2 rows in set (0.00 sec)
variable
变量EXECUTION_ID_和PROC_INST_ID_都是100001,100001是流程的实例id(instance_id),说明该变量作用域在整个instance上,整个流程周期内都可以使用,localVariable
设置了TASK_ID,并且设置了所在的EXECUTION_ID_,说明只能在指定的Task上使用。
既然有设置变量,那肯定有获取变量,可以先试着想想下面几行代码的输出会是什么?
System.out.println("getVariable(variable) ==>"+taskService.getVariable(taskId,"variable"));
System.out.println("getVariable(localVariable) ==>"+taskService.getVariable(taskId,"localVariable"));
System.out.println("getVariableLocal(variable) ==>"+taskService.getVariableLocal(taskId,"variable"));
System.out.println("getVariableLocal(localVariable) ==>"+taskService.getVariableLocal(taskId,"localVariable"));
输出的结果如下:
getVariable(variable) ==>this is Variable
getVariable(localVariable) ==>this is Local Variable
getVariableLocal(variable) ==>null
getVariableLocal(localVariable) ==>this is Local Variable
通过getVariable既能够获取到执行变量,也能获取到本地变量,通过getVariableLocal只能获取到本地变量。至于为什么Local的变量用getVariable能获取到,我们可以看下getVariable执行的sql过程
#1. 尝试用名称和taskid进行查询(即查询本地变量)
==> Preparing: select * from ACT_RU_VARIABLE where TASK_ID_ = ? and NAME_= ?
==> Parameters: 100015(String), variable(String)
<== Total: 0
#2. 如果没有查询到尝试查询执行变量(指定TASK_ID_为NULL,设置EXECUTION_ID_和NAME_)
==> Preparing: select * from ACT_RU_EXECUTION where ID_ = ?
==> Parameters: 100008(String)
<== Total: 1
==> Preparing: select * from ACT_RU_VARIABLE where EXECUTION_ID_ = ? and NAME_= ? and TASK_ID_ is null
==> Parameters: 100008(String), variable(String)
<== Total: 0
#3. 如果没有查询到尝试查询当前EXECUTION的父EXECUTION的变量,直至顶级EXECUTION,也就是instance了
==> Preparing: select * from ACT_RU_EXECUTION where ID_ = ?
==> Parameters: 100003(String)
<== Total: 1
==> Preparing: select * from ACT_RU_VARIABLE where EXECUTION_ID_ = ? and NAME_= ? and TASK_ID_ is null
==> Parameters: 100003(String), variable(String)
<== Total: 0
==> Preparing: select * from ACT_RU_EXECUTION where ID_ = ?
==> Parameters: 100001(String)
<== Total: 1
==> Preparing: select * from ACT_RU_VARIABLE where EXECUTION_ID_ = ? and NAME_= ? and TASK_ID_ is null
==> Parameters: 100001(String), variable(String)
<== Total: 1
getVariable会尝试从本地变量到执行变量到实例变量层层往上找,直到找到为止。getVariableLocal的执行过程如下:
==> Preparing: select * from ACT_RU_VARIABLE where TASK_ID_ = ? and NAME_= ?
==> Parameters: 100015(String), variable(String)
<== Total: 1
getVariableLocal直接根据TASK_ID_和NAME_进行查找,如果没找到就没找到,不会继续往上执行。
基于以上结果,我们可以得出如下结论
- TaskService.getVariable可以获取该EXECUTION链上的任意变量
- TaskService.getVariableLocal只能获取本地变量,也就是定义在TASK的变量
- TaskService.getVariable因为存在递归调用,性能没有getVariableLocal好,所以在流程里应该优先考虑使用本地变量
RuntimeService变量
流程发起
流程发起时传入的变量,保存到流程实例里在整个流程流转周期内都可以访问到,下面是一个带参数的流程发起代码示例:
public String start() {
Map params = new HashMap<>();
params.put("startVariable", "this is startVariable");
ProcessInstance instance = runtimeService.startProcessInstanceByKey("myProcess1", params);
return instance.getId();
}
存储到数据库中的变量如下
mysql> select * from activiti.ACT_RU_VARIABLE t where t.proc_inst_id_=100001 and name_='startVariable'\G;
*************************** 1. row ***************************
ID_: 100002
REV_: 1
TYPE_: string
NAME_: startVariable
EXECUTION_ID_: 100001
PROC_INST_ID_: 100001
TASK_ID_: NULL
BYTEARRAY_ID_: NULL
DOUBLE_: NULL
LONG_: NULL
TEXT_: this is startVariable
TEXT2_: NULL
1 row in set (0.00 sec)
EXECUTION_ID_
和PROC_INST_ID_
都是100001
,而100001
正是流程的instanceid,因此该变量是保存到instance中,流程的任意节点都使用到,根据上面对getVariable的分析我们知道,activiti会逐级往上寻找变量,而instance位于最顶级,因此流程内所有的节点都可以使用。
Execution变量
同样,我们可以通过示例代码观察activiti设置在Execution上的情况。
runtimeService.setVariable(task.getExecutionId(), "runtimeVariable", "this is runtimeVariable");
runtimeService.setVariableLocal(task.getExecutionId(), "runtimeLocalVariable", "this is Local runtimeLocalVariable");
System.out.println("getVariable(variable) ==>"+runtimeService.getVariable(task.getExecutionId(),"runtimeVariable"));
System.out.println("getVariable(localVariable) ==>"+runtimeService.getVariable(task.getExecutionId(),"runtimeLocalVariable"));
System.out.println("getVariableLocal(variable) ==>"+runtimeService.getVariableLocal(task.getExecutionId(),"runtimeVariable"));
System.out.println("getVariableLocal(localVariable) ==>"+runtimeService.getVariableLocal(task.getExecutionId(),"runtimeLocalVariable"));
输出为:
getVariable(variable) ==>this is runtimeVariable
getVariable(localVariable) ==>this is Local runtimeLocalVariable
getVariableLocal(variable) ==>null
getVariableLocal(localVariable) ==>this is Local runtimeLocalVariable
getVariable数据库执行过程
#基于当前EXECUTION,根据EXECUTION_ID_和NAME_进行查询,并且非本地变量(TASK_ID_ is null )
==> Preparing: select * from ACT_RU_EXECUTION where ID_ = ?
==> Parameters: 100009(String)
<== Total: 1
==> Preparing: select * from ACT_RU_VARIABLE where EXECUTION_ID_ = ? and NAME_= ? and TASK_ID_ is null
==> Parameters: 100009(String), runtimeVariable(String)
<== Total: 0
#根据父EXECUTION进行查询
==> Preparing: select * from ACT_RU_EXECUTION where ID_ = ?
==> Parameters: 100003(String)
<== Total: 1
==> Preparing: select * from ACT_RU_VARIABLE where EXECUTION_ID_ = ? and NAME_= ? and TASK_ID_ is null
==> Parameters: 100003(String), runtimeVariable(String)
<== Total: 0
#继续往上查询
==> Preparing: select * from ACT_RU_EXECUTION where ID_ = ?
==> Parameters: 100001(String)
<== Total: 1
==> Preparing: select * from ACT_RU_VARIABLE where EXECUTION_ID_ = ? and NAME_= ? and TASK_ID_ is null
==> Parameters: 100001(String), runtimeVariable(String)
<== Total: 1
getVariableLocal数据库执行过程
# 基于当前EXECUTION,根据EXECUTION_ID_和NAME_进行查询,并且非本地变量(TASK_ID_ is null )
==> Preparing: select * from ACT_RU_VARIABLE where EXECUTION_ID_ = ? and NAME_= ? and TASK_ID_ is null
==> Parameters: 100009(String), runtimeVariable(String)
<== Total: 0
基于以上结果,可以得知,RuntimeService.getVariable基于当前EXECUTION所在的EXECUTION链进行一级级往上找,直到找到符合条件的变量,getVariableLocal只找当前EXECUTION的变量,过程基本和TaskService.getVariable以及TaskService.getVariableLocal类似
临时变量
临时变量不做持久化存储,也就是不存储数据库,TaskService和RuntimeService都可以设置临时变量,设置临时变量api如下:
void setTransientVariable(String variableName, Object variableValue);
void setTransientVariableLocal(String variableName, Object variableValue);
void setTransientVariables(Map transientVariables);
void setTransientVariablesLocal(Map transientVariables);
Object getTransientVariable(String variableName);
Object getTransientVariableLocal(String variableName);
Map getTransientVariables();
Map getTransientVariablesLocal();
获取临时变量也可以通过getVariable获取,实际上getVariable首先从临时变量中读取,读不到才进行数据库查询,因此如果临时变量和执行变量或者本地变量名称一样的话,实际上读取到的是临时变量。
临时变量因为是存储在内存中的,因此当流程流到等待状态(wait state)的节点,如UserTask节点,临时变量就失效,基于这个特性,可以将临时变量作为Service中间结果的保存。