在 JBPM 架構中,變量按作用域劃分為流程實例變量(Process Instance Variable)與任務實例變量(Task Instance Variable)。二者在存儲機制、可見性、映射配置上存在本質差異。本文從底層實現、API 行為、配置映射三個維度,徹底釐清兩者關係。
| 類型 | API | 持久化 | 生命週期 | 用途 |
|---|---|---|---|---|
| 瞬時變量 | setTransientVariable() / getTransientVariable() | 不持久化,不寫入 jbpm_variableinstance | 隨流程實例銷毀而消失 | 臨時計算中間值、敏感數據不落庫 |
| 普通變量 | setVariable() / setVariableLocally() / getVariable() | 持久化至 jbpm_variableinstance | 流程實例全生命週期 | 業務數據、表單數據、流程狀態 |
// 方式一:setVariable() - 全局可見
contextInstance.setVariable("foo", "fooValue");
// 存入 jbpm_variableinstance,taskinstance_ 為 NULL
// 可被流程內任意任務實例訪問
// 方式二:setVariableLocally() - 綁定當前任務
taskInstance.setVariableLocally("foo", "fooValue");
// 存入 jbpm_variableinstance,taskinstance_ = 當前任務 ID
// 同時創建同名流程變量(JBPM 設計特性)
關鍵結論:若需讓任務實例直接訪問流程變量,用 setVariable();若需變量綁定特定任務(如表單草稿),用 setVariableLocally()。
任務實例訪問變量時遵循就近原則:
// 流程實例設置
contextInstance.setVariable("globalVar", "fromProcess");
// 任務實例直接可讀
String val = taskInstance.getVariable("globalVar"); // 返回 "fromProcess"
// 任務實例寫入同名變量 -> 創建任務局部變量,遮蔽流程變量
taskInstance.setVariable("globalVar", "fromTask");
String val2 = taskInstance.getVariable("globalVar"); // 返回 "fromTask" (任務局部)
String val3 = contextInstance.getVariable("globalVar"); // 返回 "fromProcess" (流程未變)
在流程定義的 <task> 節點下配置 <controller>,定義任務變量與流程變量的同步規則:
<task name="approval">
<controller>
<variable access="read,required" name="foo" />
<variable access="read,write" name="foo" mapped-name="bar" />
</controller>
</task>
| 配置 | 任務建立時 | 任務完成時 | 備註 |
|---|---|---|---|
name="foo" 無 mapped-name | 查找流程變量 foo → 創建任務變量 foo | 任務變量 foo → 回寫流程變量 foo | 同名雙向同步 |
name="foo" mapped-name="bar" | 查找流程變量 foo → 創建任務變量 bar | 任務變量 bar → 回寫流程變量 foo | 名稱解耦,name 對應流程,mapped-name 對應任務 |
access="read" | 讀取流程值 | 不回寫 | 只讀展示 |
access="write" | 不讀取,任務變量初始為 null | 回寫任務值到流程 | 純輸入 |
access="read,write" | 讀取流程值 | 回寫任務值到流程 | 雙向綁定 |
required | - | 強制回寫,否則拋出異常 | 必填保護 |
經實測,read、write、required 的組合在 JBPM 部分版本中行為一致,核心邏輯由 read/write 決定,required 僅在完成階段做非空檢查。建議顯式聲明意圖:read、write、read,write、read,write,required。
若流程變量未在 Task Controller 映射,直接用 TaskInstance.setVariable() 設置,則 jbpm_variableinstance.taskinstance_ 為 NULL,該變量無法通過任務實例 API 訪問。
// 方案一:使用 setVariableLocally() 綁定任務
taskInstance.setVariableLocally("localVar", "value");
// 產生 taskinstance_ = 當前任務 ID,可被任務實例直接訪問
// 副作用:同時創建同名流程變量
// 方案二:在 Controller 顯式映射(推薦)
<variable name="localVar" access="read,write" />
// 語義清晰,生命週期可控
表 jbpm_variableinstance 關鍵欄位:
| 欄位 | 含義 |
|---|---|
id_ | 主鍵 |
name_ | 變量名 |
class_ | 類型類名 |
processinstance_ | 關聯流程實例 ID(流程變量非空) |
taskinstance_ | 關聯任務實例 ID(任務局部變量非空) |
bytearray_ / long_ / ... | 不同類型值存儲欄位 |
processinstance_ 有值,taskinstance_ 為 NULLtaskinstance_ 有值,processinstance_ 通常也有值(JBPM 同步機制)ContextInstance.setVariable()TaskInstance.setVariableLocally()access、mapped-name,避免隱式行為task_ 前綴),減少認知負擔jbpm_variableinstance 表的 taskinstance_ 欄位,確認變量歸屬總結:流程變量是「全局倉庫」,任務變量是「專用抽屜」。Controller 是「搬運工」,按 access 與 mapped-name 規則在兩者間搬運數據。理解底層 jbpm_variableinstance 的雙外鍵設計,能從根源上掌握變量可見性與生命週期。