OA項目中有極大可能性使用到JBPM框架解決流程控制問題,好比請假流程、報銷流程等等。java
JBPM:JBoss Business Process Management,翻譯過來就是業務流程管理。實際上就是一個java 框架。mysql
學習JBPM最重要的就是學習數據庫中的18張表,只要熟練掌握了這18張表,學習JBPM就大功告成了。sql
1、JBPM框架搭建數據庫
1.到JBPM官方網站上下載須要的jar包、源代碼、文檔等等api
http://www.jbpm.org/session
比較流行的JBPM版本是JBPM4.4,本次使用該版本的JBPM爲例。app
下載地址:http://sourceforge.net/projects/jbpm/files/jBPM%204/jbpm-4.4/框架
2.暫時不整合SSH框架,可是實際上JBPM底層使用的是Hibernate,這點是須要特別注意的(不想注意也不行,學習JBPM必須可以熟練操做Hibernate)。eclipse
3.下載jbpm4.4以後,解壓文件,將/lib文件夾中的全部jar包和根目錄下的jbpm.jar核心包都拷貝到/WEB-INF/lib文件夾中。ide
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>
(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>
我使用的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
這三種配置文件最好都放置到classpath路徑下,這樣方便程序處理。
5.至此,程序框架已經搭建完畢,下一步就是學習JBPM API了。
2、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便可,下面一張請假流程示例圖
3、流程部署
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.流程一旦部署完畢,就意味着至少已經有了一種流程定義極其下的至少一種流程定義版本。
4、流程定義(流程部署)管理
不管是流程部署仍是流程查詢,有一個核心的接口: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);
5、流程實例-任務
任務節點是流程圖中最重要的一種節點類型,它表明了一種須要處理的任務。它有節點名稱、處理人的屬性。
實際上對任務的執行動做是推進流程前進的動力。
1.啓動流程實例
(1)根據pdid啓動流程實例:根據pdid啓動流程實例實際上就是指定了一種類型(具體到版本)的流程定義,如qingjia-1,指的是qingjia流程定義的第一種版本的定義。
ProcessInstance pi=processEngine.getExecutionService()
.startProcessInstanceById("qingjia-1");
(2)根據pdkey啓動流程實例:根據pdkey啓動流程實例並不會具體到版本,因此默認採用了最高版本的流程定義,如存在qingjia-一、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()
6、流程變量
什麼是流程變量:流程變量是隨着隨着流程當中任務的執行和結束而產生的數據。好比領導批准或者不批准的理由等等。
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); } }
7、任務動態賦值執行人
以前的示例中,不管是申請人仍是執行審批的人,都是固定的,這是不符合實際的。好比申請人,申請人若是固定了,則表示只能一我的發起申請。必須有一種方法可以動態賦值申請人和審批人。
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 }
其中,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不提。
8、流程圖中的其它概念
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"; } } }
上面的方法中的邏輯就是:若是請假天數小於等於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的掌控之中。
以後有一個比較完整的項目練習描述了這種狀況。
9、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的項目小練習。