Activiti 提供的鏈式查詢 API (createHistoricTaskInstanceQuery()) 功能有限,無法直接實現「已辦任務必須有結束時間」等複雜條件。本文演示如何通過 ManagementService.getTableName() 動態獲取表名、createNativeHistoricTaskInstanceQuery() 編寫原生 SQL、parameter() 防注入綁定參數、like 模糊查詢正確姿勢,解決實戰中 90% 的複雜查詢需求。
Activiti 會將運行時任務同步到歷史表 (ACT_HI_TASKINST):
END_TIME_ 有值END_TIME_ IS NULL直接用鏈式查詢 .list() 會包含 END_TIME_ IS NULL 的「偽已辦」數據,用戶體驗極差。
@Autowired
private HistoryService historyService;
@Autowired
private ManagementService managementService;
public List findDoneTasks(String assignee, Pagination pagination) {
// 1. 動態獲取歷史任務表名 (避免硬編碼 ACT_HI_TASKINST)
String tableName = managementService.getTableName(HistoricTaskInstance.class);
// 2. 構建原生 SQL
String sql = "SELECT * FROM " + tableName + " H " +
"WHERE H.END_TIME_ IS NOT NULL " + // 核心:過濾已完成
"AND H.ASSIGNEE_ LIKE #{assignee} " + // 參數佔位符 #{paramName}
"ORDER BY H.END_TIME_ DESC";
// 3. 執行 Native Query
return historyService.createNativeHistoricTaskInstanceQuery()
.sql(sql)
.parameter("assignee", "%" + assignee + "%") // 關鍵:% 拼在參數值中,而非 SQL 中
.listPage(pagination.getStartResult(), pagination.getPageSize());
}
| 技術點 | 作用 | 注意事項 |
|---|---|---|
ManagementService.getTableName(Class) | 根據實體類動態解析對應數據庫表名 (含前綴) | 兼容不同表前綴配置,避免硬編碼 |
createNativeHistoricTaskInstanceQuery() | 進入原生 SQL 查詢模式,返回 NativeHistoricTaskInstanceQuery | 同理有 NativeTaskQuery, NativeProcessInstanceQuery 等 |
參數佔位符 #{paramName} | 防 SQL 注入,由引擎底層預編譯綁定 | **不要**在 SQL 中寫 LIKE '%#{param}%',會導致綁定失敗 |
parameter(name, value) | 綁定參數值 | 模糊查詢的 % **必須拼在 value 中** ("%" + val + "%") |
listPage(first, max) | 分頁查詢,對應 LIMIT offset, size | 需自行計算 offset = (page-1)*size |
List all = historyService.createHistoricTaskInstanceQuery()
.taskAssignee(assignee)
.orderByHistoricTaskInstanceEndTime().desc()
.list(); // 查出所有再過濾
return all.stream()
.filter(t -> t.getEndTime() != null) // 過濾 END_TIME_ IS NOT NULL
.skip(offset)
.limit(pageSize)
.collect(Collectors.toList());
缺點:全量拉取內存壓力大、分頁不準確、不適用大數據量。
現狀:HistoricTaskInstance **無** getProcessDefinitionKey() 方法;HistoricProcessInstance 也無此字段 (僅 ProcessInstance 有)。
// 啟動流程時設置
runtimeService.startProcessInstanceByKey(
"leaveProcess", // 流程定義 Key = 業務類類名
"LeaveService.1001" // BUSINESS_KEY = 業務類名.業務ID
);
// 查詢時解析
HistoricProcessInstance hpi = historyService.createHistoricProcessInstanceQuery()
.processInstanceId(task.getProcessInstanceId())
.singleResult();
String businessKey = hpi.getBusinessKey(); // "LeaveService.1001"
String processDefKey = businessKey.split("\\.")[0]; // "LeaveService"
前提:啟動流程時約定 BUSINESS_KEY = 流程Key.業務ID,且流程 Key 即業務類名。
databaseTablePrefix=ACT_,硬編碼表名會導致遷移失敗,必須用 ManagementService.getTableName()LIKE #{param} + parameter("param", "%" + val + "%"),切勿寫成 LIKE '%#{param}%'HistoricTaskInstance 實體,**僅映射表中存在的列**,自定義字段需手動 resultTransformerEND_TIME_、ASSIGNEE_ 建議加索引,大量歷史數據查詢必須走索引Activiti 原生查詢 API 覆蓋 80% 簡單場景,剩餘 20% 複雜條件 (多表連接、特定字段過濾、自定義排序) **果斷使用 Native Query**。核心三步:**動態表名 → 原生 SQL + #{參數} → parameter 綁定值(含 %)**,安全、靈活、可維護。