對(duì)
Jbpm
數(shù)據(jù)庫(kù)應(yīng)用的簡(jiǎn)單分析和在
Mysql
上實(shí)現(xiàn)的
demo
?
吳大愚
dywu_xa@sina.com
2006-10-17
?
?
適用
jbpm3.1
版本
?
對(duì)
HelloWorldDbTest
的分析
在
Eclipse
中到如
jbpm-starters-kit-3.1\jbpm
工程后,在
src\examples\org\jbpm\db
底下有一個(gè)
HelloWorldDbTest.java
的實(shí)例。我從這個(gè)實(shí)例分析開(kāi)始,構(gòu)建自己的
jbpm
工程在
Mysql
下的應(yīng)用。
首先我們來(lái)先分析一下
HelloWorldDbTest
實(shí)例。
基本流程
HelloWorldDbTest
類是一個(gè)
JUnit
的測(cè)試類。里面只有一個(gè)測(cè)試函數(shù)
testSimplePersistence
。這個(gè)函數(shù)里面調(diào)用了三個(gè)操作,分別是:
l
????????
將流程(
processDefinition
)的部署到數(shù)據(jù)庫(kù)中;
l
????????
從數(shù)據(jù)庫(kù)中加載流程,并實(shí)例化一個(gè)流程實(shí)例(
processInstance
),然后運(yùn)行一半再將流程實(shí)例存回?cái)?shù)據(jù)庫(kù);
l
????????
從數(shù)據(jù)庫(kù)中加載流程實(shí)例,然后運(yùn)行完畢這個(gè)實(shí)例。
JbpmConfigration
在類
HelloWorldDbTest
中有靜態(tài)代碼段,內(nèi)容是構(gòu)造一個(gè)
JbpmConfigration
的實(shí)例。
JbpmConfigration
在
Jbpm3.1
中和
Jbpm3
中差別很大,
JbpmAPI
手冊(cè)描寫如下:
“
configuration of one jBPM instance. During process execution, jBPM might need to use some services. A JbpmConfiguration contains the knowledge on how to create those services.
A JbpmConfiguration is a thread safe object and serves as a factory for JbpmContexts, which means one JbpmConfiguration can be used to create JbpmContexts for all threads. The single JbpmConfiguration can be maintained in a static member or in the JNDI tree if that is available.
”
JbpmConfigration
主要有兩種加載方法,一種是從配置文件中加載,一般使用
jbpm.cfg.xml
,
jbpm-starters-kit-3.1
中放在
jbpm-starters-kit-3.1\jbpm\src\config.files
目錄下。要使用
jbpm.cfg.xml
進(jìn)行配置的話,就必須把這個(gè)文件放在
classpath
中。因?yàn)?/span>
jbpm-starters-kit-3.1\jbpm
工程已經(jīng)把此目錄放在工程的
classpath
中,所以如果在代碼中
JbpmConfiguration jbpmConfiguration = JbpmConfiguration.getInstance();
這樣書寫的話就是使用
jbpm.cfg.xml
中的配置來(lái)構(gòu)造
JbpmConfigration
。另外一種就是本例中使用的對(duì)
xml
解析的方法。
使用數(shù)據(jù)庫(kù)
在加載的
JbpmConfiguration
的加載內(nèi)容中有如下部分:
“
"? <string name='resource.hibernate.cfg.xml' " +
????? "????????? value='hibernate.cfg.xml' />" +
”
這里面的
hibernate.cfg.xml
也在
jbpm-starters-kit-3.1\jbpm\src\config.files
目錄下,是對(duì)
Jbpm
使用的
Hibernate
的配置文檔。通過(guò)這個(gè)文檔,
Jbpm
就可以通過(guò)
Hibernate
的工具,在不同的數(shù)據(jù)庫(kù)中,把說(shuō)使用到的表全部構(gòu)建出來(lái),而不需要再人工去操作
sql
語(yǔ)句在數(shù)據(jù)庫(kù)中操作,這也就是為什么這個(gè)實(shí)例中沒(méi)有需要我們自己在數(shù)據(jù)庫(kù)中建表建庫(kù)的原因。
通過(guò)
HelloWorldDbTest
類的注釋和用戶手冊(cè)中的文檔可以知道,在這個(gè)實(shí)例中使用的數(shù)據(jù)庫(kù)是一個(gè)內(nèi)存數(shù)據(jù)庫(kù),在目錄下的
Hibernate.cfg.xml
中有如下內(nèi)容:
“
???
<!-- jdbc connection properties -->
???
<
property
name
=
"hibernate.dialect"
>
org.hibernate.dialect.HSQLDialect
</
property
>
???
<
property
name
=
"hibernate.connection.driver_class"
>
org.hsqldb.jdbcDriver
</
property
>
???
<
property
name
=
"hibernate.connection.url"
>
jdbc:hsqldb:mem:.;sql.enforce_strict_size=true
</
property
>
???
<
property
name
=
"hibernate.connection.username"
>
sa
</
property
>
???
<
property
name
=
"hibernate.connection.password"
></
property
>
”
?
懂得
Hibernate
的朋友就能看出來(lái),在
jbpm
工程中,使用的數(shù)據(jù)庫(kù)是
hsqldb
。
在加載
HelloWorldDbTest
類的時(shí)候,就會(huì)發(fā)現(xiàn),在
console
有大量的輸出。其中包括:
“
12:27:38,201 [main] INFO? Configuration : Reading mappings from resource: org/jbpm/graph/def/Transition.hbm.xml
12:27:38,231 [main] INFO? HbmBinder : Mapping class: org.jbpm.graph.def.Transition -> JBPM_TRANSITION
“很多類似的輸出。這些輸出就是
Hiernate
在加載描述文件時(shí)候產(chǎn)生的說(shuō)明信息。
輸出信息中后面還包括對(duì)數(shù)據(jù)庫(kù)的連接,設(shè)置等等說(shuō)明信息。
?
JbpmConfiguration.createSchema()
這個(gè)方法可以實(shí)現(xiàn)自動(dòng)在數(shù)據(jù)庫(kù)中生成表結(jié)構(gòu)的。在這個(gè)實(shí)例中,在
Junit
的
? setup()
操作中調(diào)用了這個(gè)函數(shù),在進(jìn)行測(cè)試實(shí)例前對(duì)數(shù)據(jù)庫(kù)進(jìn)行創(chuàng)建。
JbpmContext
運(yùn)行
HelloWorldDbTest
中的測(cè)試實(shí)例。
首先是定義一個(gè)流程,然后就實(shí)例化一個(gè)
JbpmContext
。
“
JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
“
JbpmContext
類是
Jbpm3.1
版本引進(jìn)的一個(gè)新類,以前是沒(méi)有的。
在
jbpm
的說(shuō)明中有對(duì)
JbpmContext
類的如下說(shuō)明:
JbpmContext
is used to surround persistent operations to processes.
Obtain JbpmContext's via JbpmConfiguration.createJbpmContext() and put it in a try-finally block like this:
?JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
?try {
?? TaskInstance taskInstance = ...
??
?? ...do your process operations...
??
?? // in case you update a process object that was not fetched
?? // with a ...ForUpdate method, you have to save it.
?? jbpmContext.save(processInstance);
?finally {
?? jbpmContext.close();
?}
?
A JbpmContext separates jBPM from a sprecific environment. For each service that jBPM uses, there is an interface specified in the jBPM codebase. jBPM also includes implementations that implement these services by using services in a specific environment. e.g. a hibernate session, a JMS asynchronous messaging system, ...
A JbpmContext can demarcate a transaction. When a PersistenceService is fetched from the JbpmContext, the default implementation for the persistence service will create a hibernate session and start a transaction. So that transactions can be configured in the hibernate configuration.
A JbpmContext allows the user to overwrite (or make complete) the configuration by injecting objects programmatically. like e.g. a hibernate session factory or a hibernate session or any other resource that can be fetched or created from the configuration.
Last but not least, JbpmContext provides convenient access to the most common operations such as getTaskList(String), newProcessInstance(String)loadTaskInstanceForUpdate(long) and save(ProcessInstance).
All the ...ForUpdate(...) methods will automatically save the loaded objects at jbpmContext.close();
將上面這些英文讀懂,基本上就能對(duì)JbpmContext有個(gè)大致的了解。最主要的就是實(shí)現(xiàn)Jbpm對(duì)數(shù)據(jù)庫(kù)操作的所有接口的封裝。
流程部署
在
deployProcessDefinition
中的
jbpmContext. deployProcessDefinition (processDefinition);
操作,向數(shù)據(jù)庫(kù)中部署一個(gè)流程。如果跟蹤
deployProcessDefinition
的話,會(huì)發(fā)現(xiàn)
jbpmContext
還是調(diào)用的
GraphSession
的流程部署方法。后面我們?cè)僦v到
GraphSession
類。
在后面使用
Mysql
實(shí)現(xiàn)的例子中,我們就可以通過(guò)工具在
mysql
中看到這樣一個(gè)部署流程的操作對(duì)數(shù)據(jù)庫(kù)有如何影響。同樣也可以查看流程實(shí)例化保存在數(shù)據(jù)庫(kù)中的情況。
GraphSession
Jbpm
中
are the graph related database operations
的類。可以通過(guò)
GraphSession graphSession = jbpmContext.getGraphSession();
來(lái)得到這個(gè)類的實(shí)例,并且
JbpmContext
很多方法都是封裝該類的方法。
該類中包括了幾乎所有的數(shù)據(jù)庫(kù)操作的,例如部署流程,加載流程,部署流程實(shí)例,加載變量,等等。
?
HelloWorldDbTest
類本身就已經(jīng)有非常完整的注釋,
有關(guān)分析也就寫到這里。
?
Jbpm
在
Mysql
上的
demo
工程
創(chuàng)建
Jbpm Project
首先在
Eclipse
里面創(chuàng)建一個(gè)
Jbpm Process Project
的新項(xiàng)目。比如命名為
myDemo
。
Eclipse
的
Jbpm
插件會(huì)自動(dòng)創(chuàng)建一些文件,比如在
processes
目錄下有一個(gè)
simple
的流程。在
src/java
下面有一個(gè)
MessageActionHandler
的類。在
src/config.files
目錄下四個(gè)配置有文件。在
test/java
下面有一個(gè)
SimpleProcessTest
的
JUnit
類實(shí)例。可以先運(yùn)行這個(gè)測(cè)試,應(yīng)該是正確沒(méi)有問(wèn)題的。
配置
Mysql
數(shù)據(jù)庫(kù)
打開(kāi)
MySql
數(shù)據(jù)庫(kù),假設(shè)
MySql
數(shù)據(jù)庫(kù)的用戶名為
root
,密碼
1234
。首先創(chuàng)建
jbpm_db
數(shù)據(jù)庫(kù)。然后修改
src/config.files
底下的
Hibernate.cfg.xml
文件。將連接數(shù)據(jù)庫(kù)的部分換成
Mysql
的內(nèi)容。如下:
???????
<!-- jdbc connection properties -->
???
<
property
name
=
"hibernate.dialect"
>
org.hibernate.dialect.MySQLDialect
</
property
>
???
<
property
name
=
"hibernate.connection.driver_class"
>
com.mysql.jdbc.Driver
</
property
>
???
<
property
name
=
"hibernate.connection.url"
>
jdbc:mysql://localhost:3306/jbpm_db
</
property
>
???
<
property
name
=
"hibernate.connection.username"
>
root
</
property
>
???
<
property
name
=
"hibernate.connection.password"
>
1234
</
property
>
?????????????
添加代碼和流程
我們先創(chuàng)建一個(gè)自己的流程。在
processes
目錄下面創(chuàng)建一個(gè)叫做
pro1
的流程。然后添加最簡(jiǎn)單的,
start-state ,end
和一個(gè)
state
節(jié)點(diǎn)。
將
Jbpm3
工程中的
jbpm.cfg.xml
中的內(nèi)容添加到
src/config.files
目錄下的
jbpm.cfg.xml
中。
在
test/java
目錄下面添加一個(gè)新的
Junit
類。命名為
SimpleDBTest
。
代碼如下:
package com.sample;
?
import java.io.FileInputStream;
import java.util.List;
?
import junit.framework.TestCase;
?
import org.jbpm.JbpmConfiguration;
import org.jbpm.JbpmContext;
?
import org.jbpm.db.GraphSession;
import org.jbpm.graph.def.ProcessDefinition;
import org.jbpm.graph.exe.ProcessInstance;
import org.jbpm.graph.exe.Token;
?
public class SimpleDBTest extends TestCase {
?????? static JbpmConfiguration cfg = JbpmConfiguration.getInstance();????
?????? public void setUp() {
??????
?? cfg.createSchema();
?????? }????
?????? public void testDeployProcessDefinition() throws Exception {
????????????? assertNotNull("JbpmConfiguration should not be null", cfg);
???????????????????????????
????????????? FileInputStream fis = new FileInputStream("processes/pro1/processdefinition.xml");
????????????? ProcessDefinition processDefinition = ProcessDefinition.parseXmlInputStream(fis);
????????????? assertNotNull("Definition should not be null", processDefinition);
??????
??
????????????? JbpmContext jbpmContext = cfg.createJbpmContext() ;
?????????????
????????????? try {
??????
??????? // Deploy the process definition in the database
??????
??????? jbpmContext.deployProcessDefinition(processDefinition);
??????
????? } finally {
??????
????
???jbpmContext.close();
??????
????? }
?????? }
}
執(zhí)行這個(gè)操作,會(huì)發(fā)現(xiàn)報(bào)錯(cuò),報(bào)不能找到
Hibernate
的錯(cuò)誤。原來(lái)在
Eclipse
建立的
Jbpm
的工程中,雖然添加了
Jbpm
的包,但沒(méi)有添加
Hibernate
的包。因此在工程的
Java Build Path
的
Libraries
底下添加
Hibernate
的包(具體添加那些,請(qǐng)參考
Jbpm
的用戶手冊(cè))。
部署流程
執(zhí)行
SimpleDBTest .testDeployProcessDefinition()
操作。如果沒(méi)有錯(cuò)誤的話,這次就可以將流程
pro1
部署進(jìn)入
Mysql
數(shù)據(jù)庫(kù)。
在
jbpm_db
數(shù)據(jù)庫(kù)中會(huì)看到產(chǎn)生了
33
個(gè)表。查看
jbpm_processdefinition
表,會(huì)發(fā)現(xiàn)有一個(gè)名字為
pro1
,
ID
為
1
,
version
為
1
的條目。
同樣可以查看
jbpm_node
表。
再次部署
修改流程
pro1
的定義,在
state1
節(jié)點(diǎn)添加一個(gè)
Action
,在
node-enter
事件中。完整的
xml
文件見(jiàn)該文檔最后部分。然后再次運(yùn)行
SimpleDBTest .testDeployProcessDefinition()
操作。查看
jbpm_processdefinition
,會(huì)發(fā)現(xiàn)有了一個(gè)
pro1
的
version
為
2
版本的流程。這里有了一個(gè)
Jbpm
流程版本的概念,可以參考用戶手冊(cè)。
還可查看
jbpm_event
,
jbpm_action
表。
實(shí)例化流程
添加代碼:
?????? public void testLoadProcessAndInstance() throws Exception {
????????????? JbpmContext jbpmContext = cfg.createJbpmContext() ;???????????
????????????? try {
?????????????
????? GraphSession graphSession = jbpmContext.getGraphSession();??????????? ?????
?????????????
????? ProcessDefinition processDefinition =
?????????????
????????? graphSession.findLatestProcessDefinition("pro1");???????? ???
?????????????
????? ProcessInstance processInstance =
?????????????
????????? new ProcessInstance(processDefinition);
?????????????
????? Token token = processInstance.getRootToken();
?????????????
????? assertEquals("start", token.getNode().getName());
?????????????
????? // Let's start the process execution
?????????????
????? token.signal();
?????????????
????? assertEquals("state1", token.getNode().getName());
?????????????
?????
??????
??????
????? jbpmContext.save(processInstance);
?
?????????????
??? } finally {
?????????????
????? // Tear down the pojo persistence context.
?????????????
????? jbpmContext.close();
?????????????
??? }
?????? }
這段代碼把
pro1
流程的最新版本加載進(jìn)來(lái),然后實(shí)例化。并開(kāi)始執(zhí)行,到
state1
節(jié)點(diǎn)停下來(lái)(此時(shí)
Action
已經(jīng)執(zhí)行過(guò)了)。然后把這個(gè)實(shí)例也存入數(shù)據(jù)庫(kù)。
這時(shí)候查看
jbpm_processInstance
表,
jbpm_token
表。
完成實(shí)例的運(yùn)行
添加代碼
public void testLoadInstanceAndDoActionAndEnd() throws Exception {
????????????? JbpmContext jbpmContext = cfg.createJbpmContext() ;???????????
?????????????
? try {
?????????????
????? GraphSession graphSession = jbpmContext.getGraphSession();
?????????????
???
?????????????
????? ProcessDefinition processDefinition =
?????????????
????????? graphSession.findLatestProcessDefinition("pro1");
?????????????
????? List processInstances =
?????????????
????????? graphSession.findProcessInstances(processDefinition.getId());???????????? ?????
??????
?????????????
????? ProcessInstance processInstance =
?????????????
???????
??(ProcessInstance) processInstances.get(0);??????????? ?????
?????????????
????? this.assertEquals("message",(String)(processInstance.getContextInstance().getVariable("message")));
?????????????
????? processInstance.signal();
?????????????
????? assertTrue(processInstance.hasEnded());
?????????????
????? jbpmContext.save(processInstance);
?????????????
??? } finally {
?????????????
????? jbpmContext.close();
?????????????
??? }
?????? }
這段代碼將剛才的流程實(shí)例從數(shù)據(jù)庫(kù)中加載進(jìn)來(lái),然后執(zhí)行完畢。
查看表
jbpm_processinstance
表,會(huì)發(fā)現(xiàn)上次
end
字段還是
null
,現(xiàn)在已經(jīng)是填寫了剛才執(zhí)行的事件了,表示這個(gè)流程實(shí)例已經(jīng)執(zhí)行完畢。
流程
Pro1
的完整
xml
文檔
<?
xml
version
=
"1.0"
encoding
=
"UTF-8"
?>
?
<
process-definition
?
xmlns
=
"urn:jbpm.org:jpdl-3.1"?
name
=
"pro1"
>
??
<
start-state
name
=
"start"
>
?????
<
transition
name
=
""
to
=
"state1"
></
transition
>
??
</
start-state
>
??
<
state
name
=
"state1"
>
?????
<
event
type
=
"node-enter"
>
????????
<
action
name
=
"action1"
class
=
"com.sample.action.MessageActionHandler"
>
???????????
<
message
>
message
</
message
>
???????????
<
message2
>
message2
</
message2
>
????????
</
action
>
?????
</
event
>
?????
<
transition
name
=
""
to
=
"end"
></
transition
>
??
</
state
>
??
<
end-state
name
=
"end"
></
end-state
>
</
process-definition
>
總結(jié)