最近因為工作需要做一個工作流程相關的DEMO,研究了一下JBPM,記錄一下個人的心得與體會。
軟體環境:
* spring2.0.2
* hibernate3.2.2
* spring modules 0.8 (Jbpm3.1)
* jbpm3.1.4
* struts2.0.6
配置
Spring Module Jbpm模組提供了幾個工具類用來整合spring和jbpm,關於具體的配置可以參見spring module下載包中的參考手冊,按照上面的指示來就OK了,這裡粘貼樣本配置。
xml 代碼
1. <!--sp-->xml version="1.0" encoding="UTF-8"?>
2. <!--CTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd"</sp-->>
3. <beans default-autowire="byName" default-lazy-init="true">
4. <bean id="approveWorkflow"
5. class="org.springmodules.workflow.jbpm31.definition.ProcessDefinitionFactoryBean">
6. <property name="definitionLocation"
7. value="classpath:jbpm/audit/processdefinition.xml" />
8. bean>
9. <bean id="jbpmConfiguration"
10. class="org.springmodules.workflow.jbpm31.LocalJbpmConfigurationFactoryBean">
11. <property name="sessionFactory" ref="sessionFactory" />
12. <property name="configuration" value="classpath:jbpm/jbpm.cfg.xml" />
13. <property name="processDefinitions">
14. <list>
15. <ref local="approveWorkflow" />
16. list>
17. property>
18.
19. bean>
20. <bean id="jbpmTemplate"
21. class="org.springmodules.workflow.jbpm31.JbpmTemplate">
22. <constructor-arg index="0" ref="jbpmConfiguration" />
23. <constructor-arg index="1" ref="approveWorkflow" />
24. bean>
25. beans>
26.
比較關鍵的是為了能夠使JBPM實體和業務實體使用同一個會話工廠,這樣可以在JBPM流程執行個體中持久化業務實體物件。必須將業務實體映射和JBPM的實體映射進行整合。我的作法是改寫JBPM本身提供的hibernate.cfg.xml檔案,將業務實體包括在裡面。
xml 代碼
1. <!--sp-->xml version='1.0' encoding='utf-8'?>
2.
3. <!--CTYPE hibernate-configuration PUBLIC </sp-->
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. <property name="hibernate.cache.use_second_level_cache">
11. false
12. property>
13. <property name="hibernate.cache.use_query_cache">
14. false
15. property>
16. <property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialectproperty>
17. <property name="hibernate.show_sql">falseproperty>
18. <property name="hibernate.query.factory_class">
19. org.hibernate.hql.ast.ASTQueryTranslatorFactory
20. property>
21.
22.
23. <!-- 下面是業務實體映射 -->
24. <mapping resource="com/emap/jbpm/model/Apply.hbm.xml"/>
25.
26. <!-- 下面是JBPM引擎本身的實體映射 -->
27. <mapping resource="org/jbpm/graph/action/Script.hbm.xml"/>
28.
29. <mapping resource="org/jbpm/db/hibernate.queries.hbm.xml" />
30.
31. <!-- graph.def mapping files -->
32. <mapping resource="org/jbpm/graph/def/ProcessDefinition.hbm.xml"/>
33. <mapping resource="org/jbpm/graph/def/Node.hbm.xml"/>
34. <mapping resource="org/jbpm/graph/def/Transition.hbm.xml"/>
35. <mapping resource="org/jbpm/graph/def/Event.hbm.xml"/>
36. <mapping resource="org/jbpm/graph/def/Action.hbm.xml"/>
37. <mapping resource="org/jbpm/graph/def/SuperState.hbm.xml"/>
38. <mapping resource="org/jbpm/graph/def/ExceptionHandler.hbm.xml"/>
39. <mapping resource="org/jbpm/instantiation/Delegation.hbm.xml"/>
40.
41. ........
42.
43. session-factory>
44. hibernate-configuration>
下面我們看看sessionFactory工廠的配置。
xml 代碼
1. <!--sp-->xml version="1.0" encoding="UTF-8"?>
2. <!--CTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd"</sp-->>
3. <beans default-autowire="byName" default-lazy-init="true">
4.
5. <!-- 資料來源定義,使用Apache DBCP 串連池 -->
6. <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
7. <property name="driverClassName" value="${jdbc.driverClassName}"/>
8. <property name="url" value="${jdbc.url}"/>
9. <property name="username" value="${jdbc.username}"/>
10. <property name="password" value="${jdbc.password}"/>
11. bean>
12.
13. <!-- Hibernate SessionFactory -->
14. <bean id="sessionFactory"
15. class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
16. <property name="dataSource" ref="dataSource" />
17. <property name="configLocations">
18. <list>
19. <value>classpath:jbpm/hibernate.cfg.xmlvalue>
20. list>
21. property>
22. bean>
23. <!--Hibernate TransactionManager-->
24. <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
25. <property name="sessionFactory" ref="sessionFactory"/>
26. bean>
27. beans>
遺留問題
如何使用JBPM Process Designer外掛程式?
一直沒找到如何使用JBPM Process Designer外掛程式的使用文檔,比如如何配置JBPM安裝路徑,如何部署。目前唯一用到的功能就是編寫流程檔案。
如何發布流程檔案?
對於如何發布流程檔案,我比較同意如下文章中的觀點,編程實現或許是最簡潔的方式。
www.pcdog.com/edu/java/2006/11/v171946.html
如何關聯業務實體和流程執行個體?
JBPM主要用來管理商務程序,記錄每個流程進入哪個環節,同時還要儲存一些狀態,這些狀態資訊可能來自於業務實體。JBPM的實現方式是將這些狀態資訊序列化到資料庫的表列。
假定有一個訂單處理的流程,現在要擷取某個角色當前的所有工作清單,同時將關聯的訂單資訊展示給使用者,我們應該如何處理?目前我想到有以下幾種方式:
方式一:在構建任務執行個體的時候,將業務實體持久化到contextInstance,在擷取工作清單時從任務執行個體中直接解析出業務實體。如果需要儲存的業務實體資料量很大,這會給JBPM資料庫造成很大的資料冗餘。
方式二:在構建任務執行個體的時候,僅將業務實體的唯一識別碼持久到contextInstance,在擷取工作清單時從任務執行個體中解析出任務實體的唯一識別碼,然後再根據此標識符查詢業務實體資料庫。這種情況會造成查詢一個包含N個的工作清單時,需要N+1次資料庫查詢,顯然效能是無法滿足需求的。
方式三:是否可以在構建業務實體時,和TaskInstance進行關聯?這種方式會造成業務實體和JBPM緊耦合,而且必須對JBPM本身有比較深刻的理解。
有什麼更好的辦法解決這種問題呢?個人以為方式一可能是目前性價比最好的解決方式吧。
一點體會
JBPM看來在國內並沒有得到很多的應用,資料都比較稀缺,唯一的參考手冊也是非常的淺顯和簡單。而其源碼的注釋不是很好,這在國外的開源軟體中是很罕見的。
JBPM提供的某些API不是很全,舉個例子,假定我要查詢某個角色某個時間段的所有工作清單。因為TaskMgmtSession僅提供了findXXXTaskInstances(String actorId)方法,所以我只能先查詢出該角色的所有工作清單,採用如下代碼:
java 代碼
1. TaskMgmtSession taskMgmtSession = context.getTaskMgmtSession();
2. List tasks = taskMgmtSession.findPooledTaskInstances(actorId);
然後再在記憶體中使用類似如下代碼進行過濾。
java 代碼
1. if (task.getName().equals(taskName) && !task.hasEnded())
這種方式太笨拙和低效了。
如果是發布流程到資料庫的話:
JbpmConfiguration jbpmConfiguration = JbpmConfiguration.getInstance();
JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
//由 processdefinition.xml 產生相對應的流程定義類 ProcessDefinition
InputStream is = new FileInputStream(
"D:/oa/workflow/processes/PresaleHandlerForBusinessReport.xml");
ProcessDefinition processDefinition = ProcessDefinition.parseXmlInputStream(is);
try {
jbpmContext.deployProcessDefinition(processDefinition);
} catch (Exception e) {
e.printStackTrace();
} finally {
jbpmContext.close();
}
如果是卸載流程定義檔案的話:
JbpmConfiguration jbpmConfiguration = JbpmConfiguration.getInstance();
JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
try {
jbpmContext.getGraphSession().deleteProcessDefinition(Long.parseLong("5"));
} catch (Exception e) {
e.printStackTrace();
} finally {
jbpmContext.close();
}
記得jbpmContext一定要關閉。這裡有個問題,假如你要刪除的那個流程被使用的過的話,當你再去卸載這個流程的時候,這個流程未必能刪除的了。 我有的時候會碰到這樣的情況。