【Java EE 學習 67 上】【OA項目練習】【JBPM工作流的使用】


OA項目中有極大可能性使用到JBPM框架解決流程控制問題,比如請假流程、報銷流程等等。

JBPM:JBoss Business Process Management,翻譯過來就是業務流程管理。實際上就是一個java 框架。

學習JBPM最重要的就是學習數據庫中的18張表,只要熟練掌握了這18張表,學習JBPM就大功告成了。

一、JBPM框架搭建

  1.到JBPM官方網站上下載需要的jar包、源代碼、文檔等等

      http://www.jbpm.org/

    比較流行的JBPM版本是JBPM4.4,本次使用該版本的JBPM為例。

      下載地址:http://sourceforge.net/projects/jbpm/files/jBPM%204/jbpm-4.4/

  2.暫時不整合SSH框架,但是實際上JBPM底層使用的是Hibernate,這點是需要特別注意的(不想注意也不行,學習JBPM必須能夠熟練操作Hibernate)。

  3.下載jbpm4.4之后,解壓文件,將/lib文件夾中的所有jar包和根目錄下的jbpm.jar核心包都拷貝到/WEB-INF/lib文件夾中。

  4.使用到的三種配置文件

    (1)jbpm.cfg.xml,配置文件樣例:

<?xml version="1.0" encoding="UTF-8"?>

<jbpm-configuration>

  <import resource="jbpm.default.cfg.xml" />
  <import resource="jbpm.businesscalendar.cfg.xml" />
  <import resource="jbpm.tx.hibernate.cfg.xml" />
  <import resource="jbpm.jpdl.cfg.xml" />
  <import resource="jbpm.bpmn.cfg.xml" />
  <import resource="jbpm.identity.cfg.xml" />

  <!-- Job executor is excluded for running the example test cases. -->
  <!-- To enable timers and messages in production use, this should be included. -->
  <!--
  <import resource="jbpm.jobexecutor.cfg.xml" />
  -->

</jbpm-configuration>
jbpm.cfg.xml

    (2)jbpm.hibernate.cfg.xml,該文件實際上就是hibernate.cfg.xml配置文件,可以將hibernate.cfg.xml配置文件中的內容和該文件整合到一起。配置文件樣例:

 1 <?xml version="1.0" encoding="utf-8"?>
 2 
 3 <!DOCTYPE hibernate-configuration PUBLIC
 4           "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
 5           "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
 6 
 7 <hibernate-configuration>
 8   <session-factory>
 9       <!-- 
10           MySQLInnoDBDialect方式不能自動建表(忽略外鍵約束?)
11           MySQLDialect方式能夠自動建表,但是在手動結束流程實例的時候會報錯(外鍵約束不能刪除)
12           使用MySQL5InnoDBDialect解決兩方面的難題。
13        -->
14      <property name="dialect">
15         <!-- org.hibernate.dialect.MySQLDialect -->
16         org.hibernate.dialect.MySQL5InnoDBDialect
17     </property>
18     <property name="connection.url">
19         jdbc:mysql://localhost:3306/jbpm
20     </property>
21     <property name="connection.username">root</property>
22     <property name="connection.password">5a6f38</property>
23     <property name="connection.driver_class">
24         com.mysql.jdbc.Driver
25     </property>
26     <property name="myeclipse.connection.profile">mysql</property>
27     <property name="show_sql">true</property>
28     <property name="hbm2ddl.auto">update</property>
29      
30      <mapping resource="jbpm.repository.hbm.xml" />
31      <mapping resource="jbpm.execution.hbm.xml" />
32      <mapping resource="jbpm.history.hbm.xml" />
33      <mapping resource="jbpm.task.hbm.xml" />
34      <mapping resource="jbpm.identity.hbm.xml" />
35      
36   </session-factory>
37 </hibernate-configuration>
jbpm.hibernate.cfg.xml

      我使用的MySQL版本是5.5,不支持使用MySQLInnoDBDialect方言自動建表(5.1以及以下版本可以),使用MySQLDialect方言能夠實現自動建表,但是在結束流程實例的時候會報錯。

      解決這個問題的關鍵就是使用MySQL5InnoDBDialect,使用該方言就能夠實現既能夠自動建表,也能夠正常結束流程實例了。

    (3)logging.properties配置文件,該配置文件是針對log4j的配置文件,使用該配置文件能夠精確控制輸出日志信息,以方便查看詳細的工作流程。

 1 handlers= java.util.logging.ConsoleHandler
 2 redirect.commons.logging = enabled
 3 
 4 java.util.logging.ConsoleHandler.level = FINEST
 5 java.util.logging.ConsoleHandler.formatter = org.jbpm.internal.log.LogFormatter
 6 
 7 org.jbpm.level=FINE
 8 # org.jbpm.pvm.internal.tx.level=FINE
 9 # org.jbpm.pvm.internal.wire.level=FINE
10 # org.jbpm.pvm.internal.util.level=FINE
11 
12 org.hibernate.level=INFO
13 org.hibernate.cfg.SettingsFactory.level=SEVERE
14 org.hibernate.cfg.HbmBinder.level=SEVERE
15 org.hibernate.SQL.level=FINEST
16 org.hibernate.type.level=FINEST
17 # org.hibernate.tool.hbm2ddl.SchemaExport.level=FINEST
18 # org.hibernate.transaction.level=FINEST
logging.properties

    這三種配置文件最好都放置到classpath路徑下,這樣方便程序處理。

  5.至此,程序框架已經搭建完畢,下一步就是學習JBPM API了。

二、JBPM插件安裝

  1.學習JBPM必須首先安裝好JBPM插件,這樣才能制作流程圖以及部署流程。

  2.首先到下載好的jbpm4.4項目工程路徑下的/install/src/gpd文件夾下找到jbpm-gpd-site.zip文件,然后使用該文件安裝好插件

  3.安裝插件的時候可能會出現的問題:

    我使用的版本是MyEclipse10,使用該版本的MyEclipse我沒有找到Help0->install software菜單項,解決辦法:

    http://kuangdaoyizhimei.blog.163.com/blog/static/220557211201510138346395/

  4.安裝好插件之后在文件夾下右鍵new->JBPM 4 Process Definition即可,下面一張請假流程示例圖

    

三、流程部署

  1.畫最簡單的一張請假流程圖如上圖所示(沒有申請環節?不要在意這些細節)。

    首先,在空白處單擊一下,然后在properties視圖中修改流程名稱,但是Key、Version、Description字段就不要填寫了。特別是Description,寫上之后就會報錯,部署也不會成功,如果想要寫Description的話,需要切換到XML視圖,在Process根節點下添加<description></description>節點。沒有properties視圖的解決方法:百度。

    

    給每個任務節點添加執行人的方法:單擊任務節點,在properties視圖中修改

    

  2.添加完成之后一旦ctrl+s保存,就會生成一張.png圖片,在查看流程圖的時候一定會使用到該圖片。

  3.三種流程部署的方法

    (1)classpath的方式部署

ProcessEngine processEngine=Configuration.getProcessEngine();
        RepositoryService repositoryService=processEngine.getRepositoryService();
        NewDeployment newDeployment = repositoryService.createDeployment();
        newDeployment = newDeployment.addResourceFromClasspath("qingjia.jpdl.xml");
        newDeployment = newDeployment.addResourceFromClasspath("qingjia.png");
        String deploymentId=newDeployment.deploy();
        System.out.println(deploymentId);

    (2)InputStream的方式部署(比(1)方式還麻煩)

InputStream xml=this.getClass().getClassLoader().getResourceAsStream("qingjia.jpdl.xml");
        InputStream png=this.getClass().getClassLoader().getResourceAsStream("qingjia.png");
        Configuration.getProcessEngine()
        .getRepositoryService()
        .createDeployment()
        .addResourceFromInputStream("qingjia.jpdl.xml", xml)
        .addResourceFromInputStream("qingjia.png", png)
        .deploy();

    (3)ZipInputStream的方式部署(最簡單的方式,使用該種凡是需要將name.jpdl.xml和name.png打包成zip文件)

InputStream is=this.getClass().getClassLoader().getResourceAsStream("qingjia.zip");
        ZipInputStream zipInputStream = new ZipInputStream(is);
        Configuration.getProcessEngine()
        .getRepositoryService()
        .createDeployment()
        .addResourcesFromZipInputStream(zipInputStream)
        .deploy();

  4.流程部署之后,會自動生成18張表

    

  流程部署影響到的表有:jbpm4_deployment、jbpm4_deployprop、jbpm4_lob、jbpm4_property四張表

    (1)jbpm4_deployment,每部署一個流程,該表都會添加一行數據,類似於流程定義一樣,但實際上是不同流程定義的不同版本。

    

    DBID_代表主鍵,沒有什么特別的含義,起到唯一標識的作用。需要注意的只有這一個字段,標志了流程定義的ID。

    注意,同一種流程可能會有不通過的版本,每一個版本都會在這張表中有相對應的一行記錄。

    (2)jbpm4_deployprop,流程部署表,每部署一次,都會在這張表中添加四行新數據

    

    同樣的DBID_也是主鍵,沒有特別的含義,只是起到標識作用。

    DEPLOYMENT_是引用了jbpm4_deployment表DBID_的外鍵,這里都是1,表示是1這種類型的流程部署

    OBJNAME_是流程部署的名字,一般和KEY_字段的pdkey字段相同,除非特別的做了定義。

    KEY_,該字段是最重要的一個字段,四行數據的區分正是由該字段決定的。

      *  langid:流程定義語言字段,這里是jpdl-4.4表示使用的是jpdl-4.4版本的流程定義語言。

      *  pdid:流程部署標識ID,標識着本次部署,這是非常重要的字段。

      *  pdkey:流程定義標識ID,這里是qingjia,表示該字符串唯一標識了該種流程,實際上就是流程定義的名字。每一次新的部署中該值都會不同。

      *  pdversion:流程部署版本。

    (3)容易混淆的事項說明

      這張表是18張表中最核心的表之一。KEY_字段中的各個屬性值之間的關系:

      每一次新的部署都會生成一個唯一的pdkey,version的值初始化成1,同時pdid的值就變成了pdkey-version,如果下一次不是新的部署,而是有着相同pdkey的部署,則本次部署的version值就會增加,同時pdid的值就會相應的變成pdkey-version形式的值。

      綜上所述,每一次新的部署都會生成新的pdkey,如果是有相同的pdkey,則視為不同版本的流程定義,則版本號就會相應的增加,pdkey標志着唯一的流程定義,pdid唯一標志了相同流程定義下的不同版本。

    (4)jbpm4_lob,該表是存放數據的表,如果部署的時候使用了同時使用了xml配置文件和png圖片文件,則這里就會相應的增加兩行數據,分別代表着部署的xml配置文件和對應的png圖片。

      該表每一次部署中都會增加一行或者兩行新的數據。必不可少的是XML的配置文件所對應的行。

      字段DEPLOYMENT_是引用了jbpm4_deployment表的DBID_字段的外鍵。

  5.流程一旦部署完畢,就意味着至少已經有了一種流程定義極其下的至少一種流程定義版本。

 

四、流程定義(流程部署)管理

  無論是流程部署還是流程查詢,有一個核心的接口:ProcessEngine,一切的一切都從該接口出發肯定沒錯。

  1.查詢流程部署

    (1)查詢所有流程部署

List<Deployment> deploymentList=processEngine.getRepositoryService().createDeploymentQuery().list();

    查詢的表是jbpm4_deployment,通過該查詢能夠實現查找所有流程定義(不同類型流程定義的不同版本),使用Deployment接口的getId可以得到該流程定義的標識ID。注意getRepositoryService()方法。

    (2)根據部署id查詢部署,該id是jbpm4_deployment的PDID_字段的值

Deployment deployment=processEngine
        .getRepositoryService()
        .createDeploymentQuery()
        .deploymentId("1")
        .uniqueResult();

  2.查詢流程定義

    (1)查詢所有流程定義

List<ProcessDefinition> processDefinitionlist=
        processEngine.getRepositoryService()
        .createProcessDefinitionQuery()
        .list();

    (2)根據部署id查詢流程定義

ProcessDefinition processDefinition=processEngine.getRepositoryService()
        .createProcessDefinitionQuery()
        .deploymentId("1")
        .uniqueResult();

  3.查詢流程定義和查詢流程部署之間的區別

    一個流程定義可以有多個流程部署,但是一個流程部署只能屬於一個流程定義,流程定義和流程部署之間是一對多的關系。

    在jbpm4_deployprop表中的KEY_字段的pdkey值唯一的標識了一種流程定義,查詢流程定義查詢的就是jbpm4_deployprop表。

  4.根據pdkey查詢流程定義

List<ProcessDefinition> processDefinitions=processEngine.getRepositoryService()
        .createProcessDefinitionQuery()
        .processDefinitionKey("qingjia")
        .list();

    能夠想象的出,如果一種流程定義部署了多次,那么通過pdkey查詢出來的流程定義一定是多種流程定義。

  5.流程定義和流程部署之間的關系

    流程定義實際上就是流程類型+版本號,每一種流程定義都對應着一次流程部署。部署的過程也就是定義的過程。

  6.刪除流程部署

processEngine.getRepositoryService().deleteDeploymentCascade("20001");

    刪除流程部署的時候需要提供一個部署id,該id實際上就是jbpm4_deployment表中的DBID_字段的值。

  *  疑問:有沒有刪除流程定義的方法?實際上刪除流程部署的同時,也就是刪除了流程定義了,所以只需要提供一種方法就行了。這是個人理解。

  7.獲取流程圖的方法,每一種流程定義(流程部署)都會有相應的流程圖,怎么獲取該流程圖呢?

processEngine.getRepositoryService().getResourceAsStream(deploymentId, resourceName);

五、流程實例-任務

  任務節點是流程圖中最重要的一種節點類型,它代表了一種需要處理的任務。它有節點名稱、處理人的屬性。

  實際上對任務的執行動作是推動流程前進的動力。

  1.啟動流程實例

    (1)根據pdid啟動流程實例:根據pdid啟動流程實例實際上就是指定了一種類型(具體到版本)的流程定義,如qingjia-1,指的是qingjia流程定義的第一種版本的定義。

ProcessInstance pi=processEngine.getExecutionService()
        .startProcessInstanceById("qingjia-1");

    (2)根據pdkey啟動流程實例:根據pdkey啟動流程實例並不會具體到版本,所以默認采用了最高版本的流程定義,如存在qingjia-1、qingjia-2兩種流程定義,那么使用qingjia的pdkey啟動流程的時候,就會采用qingjia-2的流程定義。

ProcessInstance pi=processEngine.getExecutionService()
        .startProcessInstanceByKey("qingjia");

  2.完成任務:完成任務需要提供任務的編號,即id屬性值

processEngine.getTaskService().completeTask("30002");

  3.查詢所有的流程實例

List<ProcessInstance> processInstanceList=processEngine.getExecutionService()
        .createProcessInstanceQuery()//后面可以接上多個過濾條件,如piid,pdid,pdkey
        .list();

  注意,一種流程定義可以有多個流程實例。如一個請假流程可以同時又多個流程實例在運行,如張三在請假對應一個流程實例,李四請假也對應一個流程實例,兩個流程實例可以對應一個流程定義。

  4.查詢當前正在執行的所有任務

List<Task> taskList=processEngine
        .getTaskService()
        .createTaskQuery()
        .list();

  5.多條件查詢任務

        List<Task> taskList=processEngine
        .getTaskService()
        .createTaskQuery()
//        .assignee("張三")        //根據執行人查詢任務
//        .processDefinitionId("qingjia-3")        //根據pdid查詢任務,可以有一個或者多個
//        .processInstanceId("qingjia.40001")     //根據piid查詢任務,有唯一的一個或者多個(多個的情況是fork/join的情況)
        .list();

  6.根據任務id查詢任務

Task task=processEngine.getTaskService().getTask("50001");

    可能會對此產生疑問,為什么不能在5中的代碼中直接類似於

List<Task> taskList=processEngine
        .getTaskService()
        .createTaskQuery()
        .taskId("50001")
        .unique();

    的方式查詢。這是jbpm4.4版本的一個小瑕疵,但是並沒有錯,知道就好。

  7.查詢已經完成的所有任務

List<HistoryTask> historyTaskList=
        processEngine
        .getHistoryService()
        .createHistoryTaskQuery()
        .state(HistoryTask.STATE_COMPLETED)
        .list();

  8.直接結束流程實例,表示拒絕請求

processEngine.getExecutionService()
        .endProcessInstance("qingjia.30001", "拒絕請假");

    注意,這里數據庫方言的設置會影響執行的結果,如果數據庫方言設置成為MySQLDialect,則會拋出異常;必須使用MySQLInnoDBDialect數據庫方言,mysql5.5極其之上的版本強烈推薦使用MySQL5InnoDBDialect,這樣既能夠自動建表而且在完成任務或者拒絕任務請求的時候不會報錯。

  9.怎么判斷一個流程實例是否已經結束:根據piid查詢流程實例,如果查詢到的結果是NULL,則表示流程實例已經結束。

processEngine.getExecutionService().createProcessInstanceQuery().processInstanceId("qingjia.30001").uniqueResult()

六、流程變量

  什么是流程變量:流程變量是隨着隨着流程當中任務的執行和結束而產生的數據。比如領導批准或者不批准的理由等等。

  1.流程變量的生命周期

    從流程實例開始到流程實例結束,流程變量依附於流程實例。

  2.流程變量的保存位置:jbpm4_variable表。表中有幾個重要字段

    *  EXECUTION_:是應用了`jbpm4_execution`表id的外鍵。

    *  `TASK_`:是引用了`jbpm4_task`表id的外鍵

    *  KEY_:由於保存流程變量的時候必須使用Map,所以該KEY是對應着該Map對象的KEY值。

    *  由於可以保存的對象可以任意,所以對象的值的類型也是多種多樣。該表的多個字段提供了多種數據類型的保存。

        如LONG_VALUE_、STRING_VALUE_等。

  3.流程變量放入到流程實例中的時機

    (1)啟動實例的時候

    (2)完成任務的時候

    (3)流程實開始之后,結束之前

  4.啟動流程實例的時候放入流程變量

Person p=new Person();
        p.setId(1L);
        p.setName("person-zhangsan");
        Map<String,Person> variables=new HashMap<String,Person>();
        variables.put("person", p);
        processEngine.getExecutionService()
        .startProcessInstanceById("qingjia-1", variables);

    運行完成該段代碼之后,查看表中的數據

    

    特別一個字段CONVERTER_,該字段中的值是ser-bytes,為什么是該值呢,實際上該值是seriable-bytes,在上面的程序中,保存的值是Person對象,在保存到數據庫的時候,會將該對象序列化成二進制數據(瞧,這名字起得多好~)。

  5.完成任務的時候放入流程變量

Map<String,Object> variables=new HashMap<String,Object>();
        variables.put("請假人", "小張");
        variables.put("請假天數", 4);
        processEngine.getTaskService()
        .setVariables("30001", variables);
        processEngine.getTaskService()
//        .completeTask("30001",variables);//提供了該API,但是並不能使用,結結實實的一個bug
        .completeTask("30001");

    這里,很明顯的和啟動流程實例的時候放入流程變量的方法不同,是先放入流程變量,后完成任務,為什么不完成任務的同時放入流程變量呢(使用completeTask方法同時完成任務和將流程變量放入流程實例),實際上completeTask方法有一個重載方法,completeTask(taskId,variables),本來根據官方提供的API解釋可以使用該方法類完成任務的同時將流程變量放入到流程實例中,然而實際上調用該方法的時候就會報錯。必須在完成方法之前先將流程變量放入到流程實例中。這實際上是JBPM4.4中的一個BUG

  6.直接放入到流程實例中,不管是什么階段(只要流程實例沒有結束)

processEngine.getExecutionService()
        .setVariable("qingjia.10001","請假理由","踢足球");

  7.獲取流程變量的方法

    (1)根據taskId獲取流程變量,使用該方法獲取到的流程變量是所有的流程變量;另外需要注意,在使用該種方法獲取流程變量的時候必須保證該任務在任務表中

Set<String> variableNames=processEngine.getTaskService()
        .getVariableNames("40003");
        for(String variableName:variableNames){
            Object obj=processEngine.getTaskService().getVariable("40003", variableName);
            if(obj instanceof Person){
                Person person=(Person) obj;
                System.out.println(variableName+":"+person.getName());
            }else{
                System.out.println(variableName+":"+obj);
            }
        }

    (2)根據流程實例獲取流程變量,個人比較偏向使用這種方法,只要流程實例不結束,都可以根據該方法獲取流程實例。

Set<String> variableNames=processEngine.getExecutionService()
        .getVariableNames("qingjia.10001");
        for(String variableName:variableNames){
            Object obj=processEngine.getTaskService().getVariable("40003", variableName);
            if(obj instanceof Person){
                Person person=(Person) obj;
                System.out.println(variableName+":"+person.getName());
            }else{
                System.out.println(variableName+":"+obj);
            }
        }

 七、任務動態賦值執行人

  之前的示例中,無論是申請人還是執行審批的人,都是固定的,這是不符合實際的。比如申請人,申請人如果固定了,則表示只能一個人發起申請。必須有一種方法能夠動態賦值申請人和審批人。

  1.使用標簽指定給執行人賦值的動態方法

    這時候,不需要給任務執行人賦值。

    

    切換到XML模式,在相應的任務中添加下面的數據,如圖所示:      

      

    根據標簽中的內容,需要在指定的位置新建類,該類必須實現AssignmentHandler接口。

 1 import org.jbpm.api.model.OpenExecution;
 2 import org.jbpm.api.task.Assignable;
 3 import org.jbpm.api.task.AssignmentHandler;
 4 
 5 public class MyAssignmentHandler implements AssignmentHandler{
 6     private static final long serialVersionUID = 4278893247283956881L;
 7 
 8     @Override
 9     public void assign(Assignable assignable, OpenExecution execution)
10             throws Exception {
11         String assignableId=execution.getVariable("組長").toString();
12         assignable.setAssignee(assignableId);
13     }
14 
15 }
org.jbpm.api.task.AssignmentHandler.java

    其中,execution參數提供了獲取流程變量的方法;assignable提供了設置執行人的方法。

    比如在啟動流程實例的時候:

Map<String,String> variables=new HashMap<String,String>();
        variables.put("組長", "王二麻子");            //通過MyAssignmentHandler類獲取到參數並賦值給組長執行人
        processEngine.getExecutionService()
        .startProcessInstanceById("dynamictAssignment-3", variables);

     很明顯,使用這種方法和之前設置流程變量到流程實例中的方法是相同的。

  2.使用使用EL表達式動態賦值任務執行人。

    使用方法:#{name}

    

    使用方法:

Map<String,String> variables=new HashMap<String,String>();
        variables.put("jingli", "王二麻子他哥");
        processEngine.getTaskService().setVariables("100003", variables);
        processEngine.getTaskService()
        .completeTask("100003");

    這個方法和上面的方法有什么不同之處呢?

    這里使用的是TaskService(任務Service),而上面使用的是ExecutionService(流程實例Service),應用場景完全不相同;

    另外,這個方法本來可以在完成任務的時候同時加入流程變量,但是會出錯,這是bug不提。

八、流程圖中的其它概念

  1.組任務

    一個任務可能有多個候選人,但是完成任務的人只有一個,這樣的任務就是組任務。以電腦維修為例。

    

    上圖所示,完成上圖之后,就在流程圖上實現了組任務所需要做的事情。

    部署完成流程圖之后:

    (1)根據任務ID查詢候選人

List<Participation> participations=processEngine.getTaskService()
        .getTaskParticipations("10002");
        System.out.println(participations.size());
        for(Participation participation:participations){
            System.out.println(participation.getUserId());
        }

    (2)根據候選人查詢任務

List<Task> tasks=processEngine.getTaskService()
                .findGroupTasks("張三");
        for(Task task:tasks){
            System.out.println(task.getName());
        }

    (3)使用takeTask方法接收任務

processEngine.getTaskService()
        .takeTask("10002", "張三");

      takeTask方法的第一個參數是taskId,第二個參數是候選人,這里值得一提的是,候選人參數不一定真的是候選人,就算不是候選人列表中的也可以賦值到執行人。

    這也算的上是JBPM4.4的一個BUG了吧。

  2.transition

    transition是XML配置文件中的一個標簽名稱,該標簽名稱對應着流程圖中的一個箭頭指向。下面舉一個比較復雜的流程圖的例子。

     

    在上圖中,在任務節點項目組長請假審批中,下一個走向的節點並不確定,但是可以通過程序控制指定下一個走向的節點。

    部署流程之后:

    加入想要下一個流向總經理請假批准任務節點,則使用重載方法completeTask(taskId,outcome)指定走向的節點

String outcome="to 總經理請假批准";
        processEngine.getTaskService().completeTask("10003", outcome);

    outcome的值就是transition標簽的name值。

    執行完成上面的代碼之后,流程的任務節點就流向了總經理請假批准節點。其它以此類推。

  3.state節點

    state節點和task節點的形狀完全相同,但是其作用卻不相同。state節點沒有執行人。如果流程有分支,則必須手動指定下一個走向的節點,使用的方法並不是之前的completeTask方法了,因為這不是Task節點,而是State節點。

    

    相應的,狀態節點並不會保存到Task表中(因為不是任務節點),但是可以在jbpm4_hist_actinst表中找到相關信息。

    (1)單分支情況完成節點“任務”

processEngine.getExecutionService()
        .signalExecutionById("state.50001");

      這里使用signalExecutionById來完成,id在jbpm4_hist_actinst表中查找。

    (2)多分支的情況完成節點“任務”

      這里使用的套路和之前相同。使用signalExecutionById的重載方法即可。

processEngine.getExecutionService()
        .signalExecutionById("state.50001","to 狀態3");

  4.decision節點

    

  如圖所示的X號就是decision節點;它的作用就是根據條件選擇將流程走向那一個節點。切換到XML視圖

    

    高亮顯示的對應的就是decision節點,判斷走向那一個節點的邏輯處理就放在了對應的類中。

import org.jbpm.api.jpdl.DecisionHandler;
import org.jbpm.api.model.OpenExecution;

public class MyDecisionHandler implements DecisionHandler{
    private static final long serialVersionUID = 1521490219386641184L;
    @Override
    public String decide(OpenExecution execution) {
        int days=(Integer) execution.getVariable("days");
        if(days<=3){
            return "to end1";
        }else{
            return "to task2";
        }
    }
}
com.kdyzm.decision.MyDecisionHandler.java

    上面的方法中的邏輯就是:如果請假天數小於等於3天,直接結束流程實例;如果請假天數大於3天,則還需要task2節點批准。

   在完成task3的時候使用的代碼:

Map<String,Object> variables=new HashMap<String,Object>();
        variables.put("days", 4);
        processEngine.getTaskService()
        .setVariables("20001", variables);
        processEngine.getTaskService()
        .completeTask("20001");

    總結:實際上不使用decision節點也能完成任務,但是為了讓業務邏輯處理和流程跳轉分離,使用該節點是最好的方法

  5.fork/join節點

    fork、join結點是兩個節點。但是這兩個節點是必須相互搭配使用的兩個節點。

    這兩個節點的使用場合是同一個申請同時需要給多個人審批,最后只有多個人都同意之后流程才能走向下一個節點的這種場合,如下圖所示:

    

    再上圖中,在發起申請之后,申請的問價必須同時由項目組長和項目副組長同時同意之后流程實例才能走向總經理審批的流程環節。

    使用該方式完全不需要考慮流程的走向問題,流程的走向和處理方式完全和之前的一模一樣,不需要考慮fork節點或者join節點的存在,一切都在jbpm的掌控之中。

    之后有一個比較完整的項目練習描述了這種情況。

九、Event(事件)

  JBPM提供了多種多樣的事件,比如流程開始的時候會觸發事件、流程結束的時候也會觸發時間,進入一個任務節點的時候會觸發一個事件,離開一個任務節點的時候也會觸發事件等等。

  事件的定義方式如下圖所示:

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 
 3 <process name="event" xmlns="http://jbpm.org/4.4/jpdl">
 4     <!-- 流程級別的開始事件 -->
 5     <on event="start">
 6         <event-listener class="com.kdyzm.event.PIStartLisener"></event-listener>
 7     </on>
 8     <!-- 流程級別的結束事件 -->
 9     <on event="end">
10         <event-listener class="com.kdyzm.event.PIEndListener"></event-listener>
11     </on>
12    <start name="start1" g="533,57,48,48">
13            <!-- 開始節點的結束事件 -->
14            <on event="end">
15                <event-listener class="com.kdyzm.event.StartNodEndListener"></event-listener>
16            </on>
17       <transition name="to task1" to="task1" g="-52,-22"/>
18    </start>
19    <end name="end1" g="534,381,48,48">
20            <!-- 結束節點的開始事件 -->
21            <on event="start">
22                <event-listener class="com.kdyzm.event.EndNodeStartListener"></event-listener>
23            </on>
24    </end>
25    <task name="task1" g="499,224,92,52">
26            <!-- 任務節點的開始事件 -->
27            <on event="start">
28                <event-listener class="com.kdyzm.event.TaskNodeStartListener"></event-listener>
29            </on>
30            <!-- 任務節點的結束事件 -->
31            <on event="end">
32                <event-listener class="com.kdyzm.event.TaskNodeEndListener"></event-listener>
33            </on>
34       <transition name="to end1" to="end1" g="-50,-22"/>
35    </task>
36 </process>

  需要注意的是每一個事件對象都需要實現一個接口:EventListener,以開始節點結束事件為例:

 1 import org.jbpm.api.listener.EventListener;
 2 import org.jbpm.api.listener.EventListenerExecution;
 3 
 4 /**
 5  * start節點結束的時候觸發
 6  * @author kdyzm
 7  *
 8  */
 9 public class StartNodEndListener implements EventListener{
10     private static final long serialVersionUID = -4132312517428245180L;
11 
12     @Override
13     public void notify(EventListenerExecution execution) throws Exception {
14         System.out.println("Start 節點結束!");
15     }
16 
17 }

  可以在重寫的方法中實現想要處理的功能,這里只是打印了一句話表示表示而已。

  JBPM基礎只有這些,下一篇是JBPM的項目小練習。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM