@[toc]
在之前的文章中,鬆哥也有和小夥伴們使用過流程變量,然而沒有和大家系統的梳理過流程變量的具體玩法以及它對應的數據表詳情,今天我們就來看看 Flowable 中流程變量的詳細玩法。
1. 為什麼需要流程變量
首先我們來看看為什麼需要流程變量。
舉一個簡單的例子,假設我們有如下一個流程:
這是一個請假流程,那麼誰請假、請幾天、起始時間、請假理由等等,這些都需要説明,不然領導審批的依據是啥?那麼如何傳遞這些數據,我們就需要流程變量。
2. 流程變量的分類
整體上來説,目前流程變量可以分為三種類型:
- 全局流程變量:在整個流程執行期間,這個流程變量都是有效的。
- 本地流程變量:這個只針對流程中某一個具體的 Task(任務)有效,這個任務執行完畢後,這個流程變量就失效了。
- 臨時流程變量:顧名思義就是臨時的,這個不會存入到數據庫中。
在接下來的內容中,我會跟大家挨個介紹這些流程變量的用法。
3. 全局流程變量
假設我們就是上面這個請假流程,我們一起來看下流程變量的設置和獲取。
3.1 啓動時設置
第一種方式,就是我們可以在流程啓動的時候,設置流程變量,如下:
@Test
void test01() {
Map<String, Object> variables = new HashMap<>();
variables.put("days", 10);
variables.put("reason", "休息一下");
variables.put("startTime", new Date());
ProcessInstance pi = runtimeService.startProcessInstanceByKey("demo01", variables);
logger.info("id:{},activityId:{}", pi.getId(), pi.getActivityId());
}
我們可以在啓動的時候為流程設置變量,小夥伴們注意到,流程變量的 value 也可以是一個對象(不過這個對象要能夠序列化,即實現了 Serializable 接口),然後在啓動的時候傳入這個變量即可。
我們在流程啓動日誌中搜索 休息一下 四個字,可以找到和流程變量相關的 SQL,一共有兩條,如下:
insert into ACT_HI_VARINST (ID_, PROC_INST_ID_, EXECUTION_ID_, TASK_ID_, NAME_, REV_, VAR_TYPE_, SCOPE_ID_, SUB_SCOPE_ID_, SCOPE_TYPE_, BYTEARRAY_ID_, DOUBLE_, LONG_ , TEXT_, TEXT2_, CREATE_TIME_, LAST_UPDATED_TIME_) values ( ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? ) , ( ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? ) , ( ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? )
INSERT INTO ACT_RU_VARIABLE (ID_, REV_, TYPE_, NAME_, PROC_INST_ID_, EXECUTION_ID_, TASK_ID_, SCOPE_ID_, SUB_SCOPE_ID_, SCOPE_TYPE_, BYTEARRAY_ID_, DOUBLE_, LONG_ , TEXT_, TEXT2_) VALUES ( ?, 1, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? ) , ( ?, 1, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? ) , ( ?, 1, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? )
從標名稱上大概就能看出來,ACT_HI_VARINST 是存儲流程執行的歷史信息的,ACT_RU_VARIABLE 則是保存流程運行時候的信息的。
我們打開 ACT_RU_VARIABLE 表來看一下:
從表中我們可以看到,每一個流程變量都有對應的流程實例 ID,這就説明這些流程變量是屬於某一個流程實例的,所以我們可以按照如下方式來查詢流程變量:
@Test
void test01() {
List<Execution> list = runtimeService.createExecutionQuery().list();
for (Execution execution : list) {
Object reason = runtimeService.getVariable(execution.getId(), "reason");
logger.info("reason:{}", reason);
}
}
對應的查詢 SQL 如下:
: ==> Preparing: select * from ACT_RU_VARIABLE WHERE EXECUTION_ID_ = ? AND TASK_ID_ is null AND NAME_ = ?
: ==> Parameters: 6fdd2007-4c3a-11ed-aa7e-acde48001122(String), reason(String)
: <== Total: 1
可以看到,這個就是去 ACT_RU_VARIABLE 表中進行查詢,查詢條件中包含了變量的名稱。
當然,我們也可以直接查詢某一個流程的所有變量,如下:
@Test
void test02() {
List<Execution> list = runtimeService.createExecutionQuery().list();
for (Execution execution : list) {
Map<String,Object> variables = runtimeService.getVariables(execution.getId());
logger.info("variables:{}", variables);
}
}
這個對應的查詢 SQL 如下:
: ==> Preparing: select * from ACT_RU_VARIABLE WHERE EXECUTION_ID_ = ? AND TASK_ID_ is null
: ==> Parameters: 6fdd2007-4c3a-11ed-aa7e-acde48001122(String)
: <== Total: 3
可以看到,這個跟上面的那個差不多,只不過少了 NAME_ 這個條件。
3.2 通過 Task 設置
我們也可以在流程啓動成功之後,再去設置流程變量,步驟如下:
首先啓動一個流程:
@Test
void test01() {
ProcessInstance pi = runtimeService.startProcessInstanceByKey("demo01");
logger.info("id:{},activityId:{}", pi.getId(), pi.getActivityId());
}
然後設置流程變量:
@Test
void test03() {
Task task = taskService.createTaskQuery().singleResult();
taskService.setVariable(task.getId(), "days", 10);
Map<String, Object> variables = new HashMap<>();
variables.put("reason", "休息一下");
variables.put("startTime", new Date());
taskService.setVariables(task.getId(),variables);
}
查詢到某一個 Task,然後設置流程變量,上面這段代碼和小夥伴們演示了兩種設置方式:
- 逐個設置
- 直接設置一個 Map
上面這個設置流程變量的方式,本質上還是往 ACT_HI_VARINST 和 ACT_RU_VARIABLE 表中插入數據。具體的 SQL 也和前面的一樣,我就不貼出來了。
3.3 完成任務時設置
也可以在完成一個任務的時候設置流程變量,如下:
@Test
void test04() {
Task task = taskService.createTaskQuery().singleResult();
Map<String, Object> variables = new HashMap<>();
variables.put("reason", "休息一下");
variables.put("startTime", new Date());
variables.put("days", 10);
taskService.complete(task.getId(),variables);
}
底層涉及到的 SQL 都跟前面一樣,我就不贅述了。
3.4 通過流程設置
由於是全局流程變量,所以我們也可以通過 RuntimeService 來進行設置,如下:
@Test
void test05() {
Execution execution = runtimeService.createExecutionQuery().singleResult();
runtimeService.setVariable(execution.getId(), "days", 10);
Map<String, Object> variables = new HashMap<>();
variables.put("reason", "休息一下");
variables.put("startTime", new Date());
runtimeService.setVariables(execution.getId(), variables);
}
好啦,一共就是這四種方式。
4. 本地流程變量
第三小節我們説的全局流程變量是和某一個具體的流程綁定的,而本地流程變量則不同,本地流程變量和某一個 Task 綁定。
4.1 通過 Task 設置
假設我們啓動流程之後,通過 Task 來設置一個本地流程變量,方式如下:
@Test
void test03() {
Task task = taskService.createTaskQuery().singleResult();
taskService.setVariableLocal(task.getId(), "days", 10);
Map<String, Object> variables = new HashMap<>();
variables.put("reason", "休息一下");
variables.put("startTime", new Date());
taskService.setVariables(task.getId(),variables);
}
上面這段代碼中,我設置了一個本地變量,兩個全局變量,設置完成後,我們去 ACT_RU_VARIABLE 表中來查看一下具體的效果。
大家看到,由於 days 是本地變量,所以它的 TASK_ID_ 有值,這個好理解,説明 days 這個變量和這個具體的 Task 是有關的。
此時如果我們完成這個 Task,代碼如下:
@Test
void test06() {
Task task = taskService.createTaskQuery().singleResult();
taskService.complete(task.getId());
}
完成之後,再來查看 ACT_RU_VARIABLE 表,如下:
我們發現本地變量 days 已經沒有了。因為上一個 Task 都已經執行完畢了,這個時候如果還是按照第三小節介紹的方式去查詢變量,就查不到 days 了。此時如果需要查詢到曾經的 days 變量,得去歷史表中查詢了,方式如下:
@Test
void test07() {
ProcessInstance pi = runtimeService.createProcessInstanceQuery().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());
}
}
這是流程本地變量的特點,當然相關的方法還有好幾個,這裏列出來給小夥伴們參考:
- org.flowable.engine.TaskService#complete(java.lang.String, java.util.Map<java.lang.String,java.lang.Object>, boolean):在完成一個 Task 的時候,如果傳遞了變量,則可以通過第三個參數來控制這個變量是全局的還是本地的,true 表示這個變量是本地的。
- org.flowable.engine.RuntimeService#setVariableLocal:為某一個執行實例設置本地變量。
- org.flowable.engine.RuntimeService#setVariablesLocal:同上,批量設置。
好啦,這就是本地流程變量。
5. 臨時流程變量
臨時流程變量是不存數據庫的,一般來説我們可以在啓動流程或者完成任務的時候使用,用法如下:
@Test
void test21() {
Map<String, Object> variables = new HashMap<>();
variables.put("reason", "休息一下");
variables.put("startTime", new Date());
ProcessInstance pi = runtimeService
.createProcessInstanceBuilder()
.transientVariable("days", 10)
.transientVariables(variables)
.processDefinitionKey("demo01")
.start();
logger.info("id:{},activityId:{}", pi.getId(), pi.getActivityId());
}
上面這段代碼涉及到的流程變量就是臨時流程變量,它是不會存入到數據庫中的。
也可以在完成一個任務的時候設置臨時變量,如下:
@Test
void test22() {
Task task = taskService.createTaskQuery().singleResult();
Map<String, Object> transientVariables = new HashMap<>();
transientVariables.put("days", 10);
taskService.complete(task.getId(), null, transientVariables);
}
這個臨時變量也是不會存入到數據庫中的。
好啦,關於流程變量,今天就和小夥伴們先説這麼多~