先看下面兩張圖: java
對以上兩張圖進行說明:mysql
經過以上分析咱們就能夠抽象成:
接下來給出工做流的書面化概念:spring
工做流(Workflow),就是「業務過程的部分或總體在計算機應用環境下的自動化」,它主要解決的是「使在多個參與者之間按照某種預約義的規則傳遞文檔、信息或任務的過程自動進行,從而實現某個預期的業務目標,或者促使此目標的實現」。sql
對於第一次接觸工做流的小夥伴來講,以爲難以理解,也無可厚非,說得好像我本身就能深入理解同樣,我也只是將學習Activiti工做流框架中的一些知識點記錄下來而已,也但願能和你們討論。數據庫
無論了,下面也給出工做流管理系統的概念:apache
工做流管理系統(Workflow Management System, WfMS)是一個軟件系統,它完成工做量的定義和管理,並按照在系統中預先定義好的工做流邏輯進行工做流實例的執行。工做流管理系統不是企業的業務系統,而是爲企業的業務系統的運行提供了一個軟件的支撐環境。api
除此以外,工做流管理聯盟(WfMC,Workflow Management Coalition)也給出了關於工做流管理系統的定義:mybatis
工做流管理系統是一個軟件系統,它經過執行通過計算的流程定義去支持一批專門設定的業務流程。工做流管理系統被用來定義、管理和執行工做流程。架構
而工做流管理系統的目標爲:框架
管理工做的流程以確保工做在正確的時間被指望的人員所執行——在自動化進行的業務過程當中插入人工的執行和干預。
說完工做流,不可避免就要闡述一下工做流框架的概念,工做流框架即用於處理工做流相關問題的框架。常見的工做流框架有:
我本人使用的是activiti5.13這個工做流框架。
Activiti5是由Alfresco軟件在2010年5月17日發佈的業務流程管理(BPM)框架,它是覆蓋了業務流程管理、工做流、服務協做等領域的一個開源的、靈活的、易擴展的可執行流程語言框架。Activiti基於Apache許可的開源BPM平臺,創始人Tom Baeyens是JBoss JBPM的項目架構師,它的特點是提供了eclipse插件,開發人員能夠經過插件直接繪畫出業務流程圖。
你們可能據說過業務流程圖一嘴,這裏給出兩個業務流程圖:
Activiti框架的目錄結構以下圖所示:
你會發現bin目錄是空的。database目錄下有3個目錄,一個目錄是create,裏面存放的是建表語句,一個目錄是drop,裏面存放的是刪除表的語句,最後一個目錄是upgrade,裏面存放的是升級Activiti的語句。libs目錄下是一些jar包,最核心的jar包是activiti-engine-5.13.jar
。wars目錄下存放的是Activiti框架官方的學習demo,咱們初次學習Activiti框架必然要借鑑其中的案例。
我想你們可能會好奇Activiti框架裏面爲什麼會有一些建表語句。由於工做流框架底層是有一套數據庫提供支持的,針對不一樣的數據庫提供不一樣的sql建表語句。Activiti5.13框架對應23張表,JBPM4.4框架對應18張表,開發人員不須要本身編寫sql語句操做這些表,框架底層會生成sql語句操做。Activiti5.13框架底層使用mybatis框架操做數據庫,JBPM框架底層使用hibernate框架操做數據庫。
要在eclipse上安裝activiti插件,可參考個人文章4.5版本eclipse安裝activiti插件!
下面我就來用這個插件設計一個請假流程圖:
【第一步】,建一個普通的java項目,例如activiti_02,在src目錄下右鍵→New
→Other...
【第二步】,在彈出的對話框中,在輸入項中輸入activiti,快速找到Activiti Diagram,選中它,點擊Next
【第三步】,在彈出的對話框中,寫入流程圖的名稱,固然了亦可以使用默認名稱——MyProcess,而後點擊Next
【第四步】,在最後彈出的對話框中直接點擊Finish
【第五步】,觀看如下gif動圖,讀者便可建立一個請假流程圖
Activiti框架提供了sql腳本文件用於建表,這些sql腳本文件就位於Activiti框架database目錄下的create目錄中,以下:
下面我就來使用Activiti框架提供的sql腳本建表,步驟以下:
【第一步】,手動建立一個數據庫
【第二步】,進入數據庫,執行框架提供的sql腳本文件
照理來講咱們可使用source
命令來執行這些sql腳本文件的,但不知爲什麼,我就是不行。我就沒糾結這個問題了,直接將如下三個sql腳本文件拖入Navicat for MySQL圖形化工具中。
在上面建立好了一個普通的java項目——activiti_02以後,要使用Activiti框架自動建表,還必須導入Activiti框架所需的jar包,那這些jar包到哪兒去找呢?還記得以前我講過Activiti框架下的wars目錄中存放的是Activiti框架官方的學習demo嗎?因此讀者可將wars目錄中的activiti-rest.war
文件解壓縮,在activiti-5.13\wars\activiti-rest\WEB-INF\lib
目錄下可找到Activiti框架所需的全部jar包,以下:
接着在activiti_02項目下新建一個lib目錄,將以上Activiti框架所需的全部jar包導入到lib目錄中,除此以外,還要導入MySQL數據庫驅動的jar包:
讀者可千萬別忘了這個jar包喲!!!
在src目錄下建立一個cn.itcast.activiti包,並在該包下編寫一個HelloWorld單元測試類,並在該類中編寫以下單元測試方法:
public class HelloWorld { /** * 使用activiti框架提供的自動建表方式建立23張表-----沒有提供配置文件 */ @Test public void test1() { // 建立一個流程引擎配置對象 ProcessEngineConfiguration conf = ProcessEngineConfiguration.createStandaloneProcessEngineConfiguration(); // 設置jdbc鏈接參數 conf.setJdbcDriver("com.mysql.jdbc.Driver"); conf.setJdbcUrl("jdbc:mysql://localhost:3306/activiti_01"); conf.setJdbcUsername("root"); conf.setJdbcPassword("yezi"); // 設置自動建表 conf.setDatabaseSchemaUpdate("true"); // 使用配置對象建立一個流程引擎對象,而且在建立過程當中能夠自動建表 ProcessEngine processEngine = conf.buildProcessEngine(); } }
執行以上test1方法,便可在activiti_01數據庫中建立好23張表。
在沒有提供xml配置文件的狀況時使用Activiti框架自動建表,我是把jdbc鏈接參數寫死在程序中的,想都不要想,這種方式是愚蠢的。更合理的作法是把這些jdbc鏈接參數配置到一個配置文件中,而不是在java代碼中寫死。
在activiti_02項目下新建一個config源碼目錄,並在該目錄下建立一個activiti-context.xml配置文件,內容以下:
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd"> <!-- 配置一個流程引擎配置對象 --> <bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration"> <property name="jdbcDriver" value="com.mysql.jdbc.Driver"></property> <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/activiti_01"></property> <property name="jdbcUsername" value="root"></property> <property name="jdbcPassword" value="yezi"></property> <property name="databaseSchemaUpdate" value="true"></property> </bean> <!-- 配置一個流程引擎工廠bean,用於建立流程引擎對象 --> <bean id="processEngine" class="org.activiti.spring.ProcessEngineFactoryBean"> <property name="processEngineConfiguration" ref="processEngineConfiguration"></property> </bean> </beans>
其實,以上id爲processEngine的bean可不用配置,固然了配了也不要緊,只不過是爲後面的學習作鋪墊而已,這裏無傷大雅啊!
接着在HelloWorld單元測試類中編寫以下單元測試方法:
public class HelloWorld { /** * 使用activiti框架提供的自動建表方式建立23張表-----提供配置文件 */ @Test public void test2() { // 得到一個流程引擎配置對象 ProcessEngineConfiguration conf = ProcessEngineConfiguration .createProcessEngineConfigurationFromResource( "activiti-context.xml", "processEngineConfiguration"); // 使用配置對象建立一個流程引擎對象,而且在建立過程當中能夠自動建表 ProcessEngine processEngine = conf.buildProcessEngine(); } }
執行以上test2方法,也可在activiti_01數據庫中建立好23張表。
在實際開發中,建議在提供默認配置文件的狀況下使用Activiti框架自動建表。但須注意:配置文件必須在類路徑的根路徑下,配置文件的名稱必須爲activiti-context.xml或者爲activiti.cfg.xml,xml配置文件中必須配置流程引擎配置對象,id必須爲processEngineConfiguration,且必須配置流程引擎工廠bean,id必須爲processEngine。
由此可知,我在config源碼目錄下編寫的activiti-context.xml配置文件徹底符合以上要求,activiti-context.xml配置文件的內容爲:
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd"> <!-- 配置一個流程引擎配置對象 --> <bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration"> <property name="jdbcDriver" value="com.mysql.jdbc.Driver"></property> <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/activiti_01"></property> <property name="jdbcUsername" value="root"></property> <property name="jdbcPassword" value="yezi"></property> <property name="databaseSchemaUpdate" value="true"></property> </bean> <!-- 配置一個流程引擎工廠bean,用於建立流程引擎對象 --> <bean id="processEngine" class="org.activiti.spring.ProcessEngineFactoryBean"> <property name="processEngineConfiguration" ref="processEngineConfiguration"></property> </bean> </beans>
這個activiti-context.xml配置文件就是Activiti核心配置文件,主要配置流程引擎建立工具的基本參數和數據庫鏈接池參數。
定義數據庫配置參數:
基於JDBC參數配置的數據庫鏈接會使用默認的MyBatis鏈接池。下面的參數能夠用來配置鏈接池(來自MyBatis參數):
在這裏我給出一個示例數據庫配置:
固然了也可使用javax.sql.DataSource。(好比,Apache Commons的DBCP):
以上就看成了解,哈哈,我也沒這樣寫過!初次學習Activiti工做流框架的小白也不須要接觸到這些配置,真到要用的時候,再回來看唄!
不說遠了,接着再在HelloWorld單元測試類中編寫以下單元測試方法:
public class HelloWorld { /** * 使用activiti框架提供的自動建表方式建立23張表-----使用默認配置文件 */ @Test public void test3() { ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); } }
執行以上test3方法,一樣也可在activiti_01數據庫中建立好23張表。
Activiti的後臺是有數據庫的支持的,全部的表都以ACT_開頭。第二部分是表示用途的兩個字母標識。用途也和服務的API對應。
這四張表很常見,基本的組織機構管理,關於用戶認證方面建議仍是本身開發一套,組件自帶的功能太簡單,使用中有不少需求難以知足。
業務流程建模與標註(Business Process Model and Notation,BPMN) ,描述流程的基本符號,包括這些圖元如何組合成一個業務流程圖(Business Process Diagram)。
首先使用流程設計器插件設計一個請假流程,讀者不妨按照以下gif動圖來設計:
在這兒特此做出申明,因爲【使用流程設計器插件設計一個請假流程.gif】大小已超過2M的限制,因此未能上傳,但讀者可點擊使用流程設計器插件設計一個請假流程.gif進行下載並查看,給你們帶來一些閱讀上的麻煩,還請諒解!!!
讀者在設計請假流程圖時,必然要知道Assignee的意思,它指定任務的辦理人。恐怕你們可能會有一個疑問:若是像上面那樣設計的話,只有張三一我的能提交請假申請,其餘人是提交不了申請的。咱們如今是爲了測試的方便,因此就指定死了,後面咱們會有辦法來動態地指定。
部署流程定義即將請假規則應用到數據庫裏面去。部署流程定義操做的數據庫表有:部署表(act_re_deployment)、流程定義表(act_re_procdef)和二進制表(act_ge_bytearray)。
想必你們確定想知道部署流程定義怎樣用代碼來實現,我在HelloWorld單元測試類中編寫以下單元測試方法:
public class HelloWorld { ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); /** * 部署流程定義——即把請假規則應用到數據庫裏面去 */ @Test public void test4() { // 建立一個部署構建器對象,用於加載流程定義文件(bpmn文件和png文件) DeploymentBuilder deploymentBuilder = processEngine.getRepositoryService().createDeployment(); deploymentBuilder.addClasspathResource("qjlc.bpmn"); deploymentBuilder.addClasspathResource("qjlc.png"); // 部署,並返回一個部署對象(其實Deployment是一個接口) Deployment deployment = deploymentBuilder.deploy(); System.out.println(deployment.getId()); } }
整個Activiti框架最核心的組件是ProcessEngine,部署流程定義就須要用到它,只要是跟工做流相關的任何操做都要使用到流程引擎對象。爲了能讓接下來編寫的全部單元測試方法都能使用到它,故使其成爲成員變量。如下這句代碼:
Deployment deployment = deploymentBuilder.deploy();
返回的是一個部署對象,注意Deployment是一個接口。其實只要一調用deploy方法,Activiti框架就會幫咱們發出sql語句,來操做數據庫表。一旦咱們部署一次,對應地就會向部署表(act_re_deployment)裏面插入一條數據,以下:
同時向流程定義表(act_re_procdef)裏面插入一條數據,以下:
流程定義表(act_re_procdef)裏面的KEY_字段很是關鍵,KEY_字段的值是由流程圖的id值來決定的。注意:KEY_這個字段表明的是流程定義的標識,也即說只要KEY_相同,那麼說明它們就是同一個流程,但版本號可能不相同。讀者不妨再部署兩次,你將看到流程定義表(act_re_procdef)就是這樣的:
查詢流程定義操做的數據表是流程定義表(act_re_procdef)。我在HelloWorld單元測試類中編寫以下單元測試方法:
public class HelloWorld { ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); /** * 查詢流程定義 */ @Test public void test5() { // 流程定義查詢對象,用於查詢流程定義表(act_re_procdef) ProcessDefinitionQuery query = processEngine.getRepositoryService().createProcessDefinitionQuery(); // 如下查詢的是全部的流程定義 List<ProcessDefinition> list = query.list(); for (ProcessDefinition pd : list) { System.out.println(pd.getId() + " " + pd.getName() + " " + pd.getVersion()); } } }
以上查詢的是全部的流程定義,咱們亦可根據流程定義的key來過濾,以下:
public class HelloWorld { ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); /** * 查詢流程定義 */ @Test public void test5() { // 流程定義查詢對象,用於查詢流程定義表(act_re_procdef) ProcessDefinitionQuery query = processEngine.getRepositoryService().createProcessDefinitionQuery(); // 根據流程定義的key來過濾 query.processDefinitionKey("qjlc"); List<ProcessDefinition> list = query.list(); for (ProcessDefinition pd : list) { System.out.println(pd.getId() + " " + pd.getName() + " " + pd.getVersion()); } } }
萬一除了要根據流程定義的key來過濾,還要排序,咋辦?以碼明示:
public class HelloWorld { ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); /** * 查詢流程定義 */ @Test public void test5() { // 流程定義查詢對象,用於查詢流程定義表(act_re_procdef) ProcessDefinitionQuery query = processEngine.getRepositoryService().createProcessDefinitionQuery(); // 根據流程定義的key來過濾 query.processDefinitionKey("qjlc"); // 添加排序條件 query.orderByProcessDefinitionVersion().desc(); List<ProcessDefinition> list = query.list(); for (ProcessDefinition pd : list) { System.out.println(pd.getId() + " " + pd.getName() + " " + pd.getVersion()); } } }
上面是根據流程定義表(act_re_procdef)的版本號來降序排列的!那萬一咱們還要分頁查詢呢?以碼明示:
public class HelloWorld { ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); /** * 查詢流程定義 */ @Test public void test5() { // 流程定義查詢對象,用於查詢流程定義表(act_re_procdef) ProcessDefinitionQuery query = processEngine.getRepositoryService().createProcessDefinitionQuery(); // 根據流程定義的key來過濾 query.processDefinitionKey("qjlc"); // 添加排序條件 query.orderByProcessDefinitionVersion().desc(); // 分頁查詢(僞代碼) query.listPage("從哪開始查", "查幾條"); List<ProcessDefinition> list = query.list(); for (ProcessDefinition pd : list) { System.out.println(pd.getId() + " " + pd.getName() + " " + pd.getVersion()); } } }
什麼是流程實例?根據某個流程定義的一次具體執行過程,就是一個流程實例。流程定義和流程實例是一對多的關係。在本例中,根據請假流程定義來具體地請一次假,就是啓動流程實例了。
啓動流程實例操做的數據表有流程實例表(act_ru_execution)、任務表(act_ru_task)。我在HelloWorld單元測試類中編寫以下單元測試方法:
public class HelloWorld { ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); /** * 啓動流程實例 */ @Test public void test6() { String processDefinitionId = "qjlc:2:104"; // 流程定義id ProcessInstance processInstance = processEngine.getRuntimeService() .startProcessInstanceById(processDefinitionId); // 根據請假流程定義來具體地請一次假,即啓動流程實例 System.out.println(processInstance.getId()); } }
運行以上方法,流程實例表(act_ru_execution)裏面就會插入一條數據,以下:
ACT_ID_字段的值意味着流程向下推動到哪一個地步了,上面表中ACT_ID_字段的值是usertask1,表示流程推動到【提交請假申請】這一步了。
除此以外,任務表(act_ru_task)裏面也會插入一條數據,以下:
從上表可知,張三有一個任務——提交請假申請要辦理。
查詢任務操做的數據表是任務表(act_ru_task)。我在HelloWorld單元測試類中編寫以下單元測試方法:
public class HelloWorld { ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); /** * 查詢任務 */ @Test public void test7() { // 任務查詢對象,操做的是任務表(act_ru_task) TaskQuery query = processEngine.getTaskService().createTaskQuery(); // 根據任務的辦理人過濾 query.taskAssignee("張三"); // 只查詢張三的任務,其餘人的任務不查 // query.taskAssignee("李四"); // query.taskAssignee("王五"); List<Task> list = query.list(); for (Task task : list) { System.out.println(task.getId() + "\t" + task.getName() + "\t" + task.getAssignee()); } } }
上面只查詢了張三的任務,其餘人的任務沒查,由於從任務表(act_ru_task)知道張三有一個任務——提交請假申請要辦理嘛。當流程一步一步向下推動,任務也會不斷髮生變化,具體地就要根據任務的辦理人來過濾了。
辦理任務操做的數據表有任務表(act_ru_task)、流程實例表(act_ru_execution)。我在HelloWorld單元測試類中編寫以下單元測試方法:
public class HelloWorld { ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); /** * 辦理任務 */ @Test public void test8() { String taskId = "304"; // 任務的id processEngine.getTaskService().complete(taskId); } }
咱們查詢出張三的任務以後,張三就要辦理它,辦理完以後,流程向下推動到【項目經理審批】這一步,故任務表(act_ru_task)就要發生變化,以下:
從上表可知,李四如今有一個任務——項目經理審批要辦理了。除此以外,流程實例表(act_ru_execution)也要發生變化,以下:
上面表中ACT_ID_字段的值是usertask2,就已經表示流程推動到【項目經理審批】這一步了。
如今咱們就要明確一個概念,流程一步一步向下推動,並非咱們去控制的,而是由工做流框架來幫咱們推動的。咱們要作的事就是將任務查出來,把它辦理完,辦理完以後,它會自動地由工做流框架來推動到下一個任務,因此,由工做流框架負責任務一步一步地向下推動,由於咱們當時已經把流程部署進去了,也即說這個規則工做流框架是知道的,因此,咱們只須要將任務查出來,把它辦理完。流程實例表(act_ru_execution)也要發生變化,ACT_ID_這個字段的值更新了,由於流程向下推動了一步,因此ACT_ID_這個字段的值也須要更新。
下面就很簡單了,將李四的任務查詢出來,而後辦理之。這樣流程向下推動到【部門經理審批】這一步,故任務表(act_ru_task)就要發生變化,以下:
從上表可知,王五如今有一個任務——部門經理審批要辦理了。除此以外,流程實例表(act_ru_execution)也要發生變化,以下:
上面表中ACT_ID_字段的值是usertask3,就已經表示流程推動到【部門經理審批】這一步了。
接着再將王五的任務查詢出來,而後辦理之。這樣一來,任務表(act_ru_task)和流程實例表(act_ru_execution)就沒有任何數據了,整個請假流程就走完了。
部署流程定義其實有兩種方式,第一種方式是加載單個的流程定義文件,正如我以前所講解的那樣。下面再來說一下這種方式,你們能夠加深印象。
在cn.itcast.activiti包下再新建一個ActivitiAPITest單元測試類,並在該類中編寫以下單元測試方法:
public class ActivitiAPITest { ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); /** * 部署流程定義 */ @Test public void test1() { DeploymentBuilder deploymentBuilder = processEngine .getRepositoryService().createDeployment(); // 方式一:加載單個的流程定義文件 deploymentBuilder.addClasspathResource("qjlc.bpmn"); deploymentBuilder.addClasspathResource("qjlc.png"); deploymentBuilder.deploy(); } }
運行以上方法,便可在部署表(act_re_deployment)裏面新增一條記錄,以下:
部署流程定義的第二種方式是加載zip壓縮文件。咱們能夠將process源碼目錄下的兩個流程定義文件壓縮爲一個zip格式的壓縮文件,好比process.zip。
如此一來,就要將test1方法修改成:
public class ActivitiAPITest { ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); /** * 部署流程定義 */ @Test public void test1() { DeploymentBuilder deploymentBuilder = processEngine .getRepositoryService().createDeployment(); // 方式二:加載zip壓縮文件 ZipInputStream zipInputStream = new ZipInputStream(this.getClass() .getClassLoader().getResourceAsStream("process.zip")); // 從類路徑下讀取process.zip壓縮文件,並把它包裝成一個輸入流 deploymentBuilder.addZipInputStream(zipInputStream ); deploymentBuilder.deploy(); } }
運行以上方法,又在部署表(act_re_deployment)裏面新增一條記錄,以下:
在ActivitiAPITest單元測試類中編寫以下單元測試方法:
public class ActivitiAPITest { ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); /** * 查詢部署信息 */ @Test public void test2() { // 部署查詢對象,查詢部署表 DeploymentQuery query = processEngine.getRepositoryService().createDeploymentQuery(); List<Deployment> list = query.list(); for (Deployment deployment : list) { System.out.println(deployment.getId() + "\t" + deployment.getDeploymentTime()); } } }
運行以上方法便可查詢出部署表(act_re_deployment)中全部的記錄。
刪除部署信息時,同時對應操做的數據庫表有部署表(act_re_deployment)、流程定義表(act_re_procdef)和二進制表(act_ge_bytearray)。我在ActivitiAPITest單元測試類中編寫以下單元測試方法:
public class ActivitiAPITest { ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); /** * 刪除部署信息 */ @Test public void test3() { String deploymentId = "801"; // 部署id processEngine.getRepositoryService().deleteDeployment(deploymentId); } }
運行以上方法,部署表(act_re_deployment)裏面ID_爲801的部署信息被刪除掉了,附帶着流程定義表(act_re_procdef)裏面DEPLOYMENT_ID_爲801的流程定義信息也被刪掉了,固然了二進制表(act_ge_bytearray)裏面DEPLOYMENT_ID_爲801的兩條記錄一樣也被刪除掉了。 void deleteDeployment(String deploymentId);
方法有一個重載方法:
void deleteDeployment(String deploymentId, boolean cascade);
先將cascade置爲false,爲了便於測試,我先啓動流程定義id爲qjlc:1:4
的流程實例, 從流程定義表(act_re_procdef)中能夠很明顯的看出該實例的DEPLOYMENT_ID_字段的值爲1,以下:
啓動流程定義id爲qjlc:1:4
的流程實例的代碼爲:
public class HelloWorld { ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); /** * 啓動流程實例 */ @Test public void test6() { String processDefinitionId = "qjlc:1:4"; // 流程定義id ProcessInstance processInstance = processEngine.getRuntimeService() .startProcessInstanceById(processDefinitionId); // 根據請假流程定義來具體地請一次假,即啓動流程實例 System.out.println(processInstance.getId()); } }
運行以上方法,流程實例表(act_ru_execution)裏面就會插入一條數據,以下:
ACT_ID_字段的值意味着流程向下推動到哪一個地步了,上面表中ACT_ID_字段的值是usertask1,表示流程推動到【提交請假申請】這一步了。
除此以外,任務表(act_ru_task)裏面也會插入一條數據,以下:
從上表可知,張三有一個任務——提交請假申請要辦理。
啓動流程實例完畢以後,把ActivitiAPITest單元測試類中的test3方法修改成:
public class ActivitiAPITest { ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); /** * 刪除部署信息 * 刪除部署信息時,同時對應操做的數據庫表有act_re_deployment、act_re_procdef、act_ge_bytearray */ @Test public void test3() { String deploymentId = "1"; // 部署id boolean cascade = false; // 是否級聯刪除,false表示不級聯刪 processEngine.getRepositoryService().deleteDeployment(deploymentId, cascade); } }
運行以上方法,這樣當刪除部署id爲1的部署信息時,就會拋出一個org.apache.ibatis.exceptions.PersistenceException
異常,可見並無刪除成功,由於有外鍵約束。
咱們就想刪除成功呢?則能夠將cascade置爲true,但不建議這麼作。再次把ActivitiAPITest單元測試類中的test3方法修改成:
public class ActivitiAPITest { ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); /** * 刪除部署信息 * 刪除部署信息時,同時對應操做的數據庫表有act_re_deployment、act_re_procdef、act_ge_bytearray */ @Test public void test3() { String deploymentId = "1"; // 部署id boolean cascade = true; processEngine.getRepositoryService().deleteDeployment(deploymentId, cascade); } }
這樣當刪除部署id爲1的部署信息時,不由發現部署表(act_re_deployment)裏面ID_爲1的部署信息被刪除掉了,附帶着流程定義表(act_re_procdef)裏面DEPLOYMENT_ID_爲1的流程定義信息也被刪掉了,還有流程實例表(act_ru_execution)和任務表(act_ru_task)中PROC_DEF_ID_字段的值爲qjlc:1:4
的記錄也被刪除掉了。
假設如今有這樣一個需求:查詢最新版本的流程定義數據。給出流程定義表(act_re_procdef),以下:
要實現這樣一個需求,特簡單,在ActivitiAPITest單元測試類中編寫以下測試方法:
public class ActivitiAPITest { ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); /** * 查詢最新版本的流程定義數據 */ @Test public void test4() { // 流程定義查詢對象,查詢的是流程定義表(act_re_procdef) ProcessDefinitionQuery query = processEngine.getRepositoryService().createProcessDefinitionQuery(); // 最新版本過濾 query.latestVersion(); List<ProcessDefinition> list = query.list(); for (ProcessDefinition processDefinition : list) { System.out.println(processDefinition.getId()); } } }
好了,回到這一小節的主題,關於如何得到流程定義文件名稱和輸入流,我我的總結爲兩種方式。
【第一種方式】,根據客戶端傳過來的部署id進行獲取。在ActivitiAPITest單元測試類中編寫以下測試方法:
public class ActivitiAPITest { ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); /** * 查詢一次部署對應的流程定義文件名稱和輸入流 * @throws IOException */ @Test public void test5() throws IOException { String deploymentId = "201"; // 部署id // 得到兩個流程定義文件的名稱 List<String> names = processEngine .getRepositoryService().getDeploymentResourceNames(deploymentId); for (String name : names) { System.out.println(name); // 得到兩個流程定義文件對應的輸入流 InputStream in = processEngine .getRepositoryService().getResourceAsStream(deploymentId, name); // 讀取輸入流寫到指定的本地磁盤上 FileUtils.copyInputStreamToFile(in, new File("F:\\" + name)); in.close(); } } }
運行以上方法,除了在Eclipse控制檯打印兩個流程定義文件的名稱,F盤上也會生成兩個流程定義文件:
【第二種方式】,根據客戶端傳過來的流程定義id進行獲取。在ActivitiAPITest單元測試類中編寫以下測試方法:
public class ActivitiAPITest { ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); /** * 得到文件名稱和輸入流 * @throws IOException */ @Test public void test6() throws IOException { String processDefinitionId = "qjlc:2:104"; // 流程定義id // 直接得到png圖片的名稱 // 根據流程定義id查詢流程定義對象 ProcessDefinitionQuery query = processEngine.getRepositoryService().createProcessDefinitionQuery(); query.processDefinitionId(processDefinitionId); ProcessDefinition processDefinition = query.singleResult(); // 根據流程定義對象得到png圖片的名稱 String pngName = processDefinition.getDiagramResourceName(); // 直接得到png圖片對應的輸入流 InputStream pngStream = processEngine.getRepositoryService().getProcessDiagram(processDefinitionId); // 讀取輸入流寫到指定的本地磁盤上 FileUtils.copyInputStreamToFile(pngStream, new File("F:\\" + pngName)); pngStream.close(); } }
這種方式只是獲取到png圖片的名稱和其對應的輸入流。
啓動流程實例可分爲兩種方式:
先講第一種方式,咱們以前啓動流程實例時就是使用的這種方式。如今再講一遍加深印象。給出流程定義表(act_re_procdef),以下:
如今咱們想啓動流程定義id爲qjlc:2:104
的流程實例,可在ActivitiAPITest單元測試類中編寫以下測試方法:
public class ActivitiAPITest { ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); /** * 啓動流程實例 */ @Test public void test7() { String processDefinitionId = "qjlc:2:104"; // 流程定義的id // 方式一:根據流程定義的id來啓動流程實例 ProcessInstance processInstance = processEngine.getRuntimeService() .startProcessInstanceById(processDefinitionId); System.out.println(processInstance.getId()); } }
再講第二種方式,這種方式也是被推薦使用的,即根據流程定義的key來啓動流程實例,該方式能夠自動選擇最新版本的流程定義來啓動流程實例。以碼明示,將ActivitiAPITest單元測試類中的test7方法改成:
public class ActivitiAPITest { ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); /** * 啓動流程實例 */ @Test public void test7() { String processDefinitionKey = "qjlc"; // 流程定義的key // 方式二:根據流程定義的key來啓動流程實例(建議)——能夠自動選擇最新版本的流程定義來啓動流程實例 ProcessInstance processInstance = processEngine.getRuntimeService().startProcessInstanceByKey(processDefinitionKey); System.out.println(processInstance.getId()); } }
運行以上方法,啓動的是流程定義id爲qjlc:4:704
的流程實例,給出流程實例表(act_ru_execution),以下:
查詢流程實例操做的是流程實例表(act_ru_execution)。在ActivitiAPITest單元測試類中編寫以下測試方法:
public class ActivitiAPITest { ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); /** * 查詢流程實例 */ @Test public void test8() { // 流程實例查詢對象,操做的是流程實例表(act_ru_execution) ProcessInstanceQuery query = processEngine.getRuntimeService().createProcessInstanceQuery(); List<ProcessInstance> list = query.list(); for (ProcessInstance processInstance : list) { System.out.println(processInstance.getId()); } } }
何謂刪除流程實例?舉個例子,某人把請假流程啓動以後,又不想請假了,那意味着後面的人就不用幫他審批了,因此就須要把這個流程實例刪除掉。如要刪除流程實例id爲1001的那個流程實例,則可在ActivitiAPITest單元測試類中編寫以下測試方法進行測試:
public class ActivitiAPITest { ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); /** * 刪除流程實例 */ @Test public void test9() { String processInstanceId = "1001"; // 流程實例id String deleteReason = "不請假了"; // 刪除緣由,任君寫 processEngine.getRuntimeService().deleteProcessInstance(processInstanceId, deleteReason); } }
運行以上方法,流程實例id爲1001的流程實例被刪除掉了,附帶着任務表(act_ru_task)裏面EXECUTION_ID_字段爲1001的那條記錄也被刪除掉了。
查詢任務對應操做的數據庫表是任務表(act_ru_task)。在ActivitiAPITest單元測試類中編寫以下測試方法進行測試:
public class ActivitiAPITest { ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); /** * 查詢任務 */ @Test public void test10() { // 任務查詢對象,對應操做的數據庫表是任務表(act_ru_task) TaskQuery query = processEngine.getTaskService().createTaskQuery(); query.taskAssignee("張三"); List<Task> list = query.list(); for (Task task : list) { System.out.println(task.getId() + "\t" + task.getName()); } } }
上面只查詢了張三的任務,其餘人的任務沒查,由於從任務表(act_ru_task)知道張三有一個任務——提交請假申請要辦理嘛。當流程一步一步向下推動,任務也會不斷髮生變化,具體地就要根據任務的辦理人來過濾了。
辦理任務操做的數據表有任務表(act_ru_task)、流程實例表(act_ru_execution)。我在ActivitiAPITest單元測試類中編寫以下單元測試方法:
public class ActivitiAPITest { ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); /** * 辦理任務 */ @Test public void test11() { String taskId = "1104"; // 任務id processEngine.getTaskService().complete(taskId); } }
咱們查詢出張三的任務以後,張三就要辦理它,辦理完以後,流程向下推動到【項目經理審批】這一步,故任務表(act_ru_task)就要發生變化,以下:
從上表可知,李四如今有一個任務——項目經理審批要辦理了。除此以外,流程實例表(act_ru_execution)也要發生變化,以下:
上面表中ACT_ID_字段的值是usertask2,就已經表示流程推動到【項目經理審批】這一步了。
如今咱們就要明確一個概念,流程一步一步向下推動,並非咱們去控制的,而是由工做流框架來幫咱們推動的。咱們要作的事就是將任務查出來,把它辦理完,辦理完以後,它會自動地由工做流框架來推動到下一個任務,因此,由工做流框架負責任務一步一步地向下推動,由於咱們當時已經把流程部署進去了,也即說這個規則工做流框架是知道的,因此,咱們只須要將任務查出來,把它辦理完。流程實例表(act_ru_execution)也要發生變化,ACT_ID_這個字段的值更新了,由於流程向下推動了一步,因此ACT_ID_這個字段的值也須要更新。
下面就很簡單了,將李四的任務查詢出來,而後辦理之。這樣流程向下推動到【部門經理審批】這一步,故任務表(act_ru_task)就要發生變化,以下:
從上表可知,王五如今有一個任務——部門經理審批要辦理了。除此以外,流程實例表(act_ru_execution)也要發生變化,以下:
上面表中ACT_ID_字段的值是usertask3,就已經表示流程推動到【部門經理審批】這一步了。
接着再將王五的任務查詢出來,而後辦理之。這樣一來,任務表(act_ru_task)和流程實例表(act_ru_execution)就沒有任何數據了,整個請假流程就走完了。