对Jbpm数据库应用的简单分析和在Mysql上实现的demo
吴大愚
2006-10-17
适用jbpm3.1版本
在Eclipse中到如jbpm-starters-kit-3.1/jbpm工程后,在src/examples/org/jbpm/db底下有一个HelloWorldDbTest.java的实例。我从这个实例分析开始,构建自己的jbpm工程在Mysql下的应用。
首先我们来先分析一下HelloWorldDbTest实例。
HelloWorldDbTest类是一个JUnit的测试类。里面只有一个测试函数testSimplePersistence。这个函数里面调用了三个操作,分别是:
l 将流程(processDefinition)的部署到数据库中;
l 从数据库中加载流程,并实例化一个流程实例(processInstance),然后运行一半再将流程实例存回数据库;
l 从数据库中加载流程实例,然后运行完毕这个实例。
在类HelloWorldDbTest中有静态代码段,内容是构造一个JbpmConfigration的实例。JbpmConfigration在Jbpm3.1中和Jbpm3中差别很大,JbpmAPI手册描写如下:
“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进行配置的话,就必须把这个文件放在classpath中。因为jbpm-starters-kit-3.1/jbpm工程已经把此目录放在工程的classpath中,所以如果在代码中JbpmConfiguration jbpmConfiguration = JbpmConfiguration.getInstance();这样书写的话就是使用jbpm.cfg.xml中的配置来构造JbpmConfigration。另外一种就是本例中使用的对xml解析的方法。
在加载的JbpmConfiguration的加载内容中有如下部分:
“"
" value='hibernate.cfg.xml' />" +”
这里面的hibernate.cfg.xml也在jbpm-starters-kit-3.1/jbpm/src/config.files目录下,是对Jbpm使用的Hibernate的配置文档。通过这个文档,Jbpm就可以通过Hibernate的工具,在不同的数据库中,把说使用到的表全部构建出来,而不需要再人工去操作sql语句在数据库中操作,这也就是为什么这个实例中没有需要我们自己在数据库中建表建库的原因。
通过HelloWorldDbTest类的注释和用户手册中的文档可以知道,在这个实例中使用的数据库是一个内存数据库,在目录下的Hibernate.cfg.xml中有如下内容:
“
<property name="hibernate.dialect">org.hibernate.dialect.HSQLDialectproperty>
<property name="hibernate.connection.driver_class">org.hsqldb.jdbcDriverproperty>
<property name="hibernate.connection.url">jdbc:hsqldb:mem:.;sql.enforce_strict_size=trueproperty>
<property name="hibernate.connection.username">saproperty>
<property name="hibernate.connection.password">property>”
懂得Hibernate的朋友就能看出来,在jbpm工程中,使用的数据库是hsqldb。
在加载HelloWorldDbTest类的时候,就会发现,在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在加载描述文件时候产生的说明信息。
输出信息中后面还包括对数据库的连接,设置等等说明信息。
这个方法可以实现自动在数据库中生成表结构的。在这个实例中,在Junit的 setup()操作中调用了这个函数,在进行测试实例前对数据库进行创建。
运行HelloWorldDbTest中的测试实例。
首先是定义一个流程,然后就实例化一个JbpmContext。
“JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext(); “
JbpmContext类是Jbpm3.1版本引进的一个新类,以前是没有的。
在jbpm的说明中有对JbpmContext类的如下说明:
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();
将上面这些英文读懂,基本上就能对JbpmContext有个大致的了解。最主要的就是实现Jbpm对数据库操作的所有接口的封装。
在deployProcessDefinition中的jbpmContext. deployProcessDefinition (processDefinition);操作,向数据库中部署一个流程。如果跟踪deployProcessDefinition的话,会发现jbpmContext还是调用的GraphSession的流程部署方法。后面我们再讲到GraphSession类。
在后面使用Mysql实现的例子中,我们就可以通过工具在mysql中看到这样一个部署流程的操作对数据库有如何影响。同样也可以查看流程实例化保存在数据库中的情况。
Jbpm中are the graph related database operations的类。可以通过GraphSession graphSession = jbpmContext.getGraphSession();来得到这个类的实例,并且JbpmContext很多方法都是封装该类的方法。
该类中包括了几乎所有的数据库操作的,例如部署流程,加载流程,部署流程实例,加载变量,等等。
HelloWorldDbTest类本身就已经有非常完整的注释,有关分析也就写到这里。
首先在Eclipse里面创建一个Jbpm Process Project的新项目。比如命名为myDemo。Eclipse的Jbpm插件会自动创建一些文件,比如在processes目录下有一个simple的流程。在src/java下面有一个MessageActionHandler的类。在src/config.files目录下四个配置有文件。在test/java下面有一个SimpleProcessTest的JUnit类实例。可以先运行这个测试,应该是正确没有问题的。
打开MySql数据库,假设MySql数据库的用户名为root,密码1234。首先创建jbpm_db数据库。然后修改src/config.files底下的Hibernate.cfg.xml文件。将连接数据库的部分换成Mysql的内容。如下:
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialectproperty>
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driverproperty>
<property name="hibernate.connection.url">jdbc:mysql://localhost:3306/jbpm_dbproperty>
<property name="hibernate.connection.username">rootproperty>
<property name="hibernate.connection.password">1234property>
我们先创建一个自己的流程。在processes目录下面创建一个叫做pro1的流程。然后添加最简单的,start-state ,end和一个state节点。
将Jbpm3工程中的jbpm.cfg.xml中的内容添加到src/config.files目录下的jbpm.cfg.xml中。
在test/java目录下面添加一个新的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();
}
}
}
执行这个操作,会发现报错,报不能找到Hibernate的错误。原来在Eclipse建立的Jbpm的工程中,虽然添加了Jbpm的包,但没有添加Hibernate的包。因此在工程的Java Build Path的Libraries底下添加Hibernate的包(具体添加那些,请参考Jbpm的用户手册)。
执行SimpleDBTest .testDeployProcessDefinition()操作。如果没有错误的话,这次就可以将流程pro1部署进入Mysql数据库。
在jbpm_db数据库中会看到产生了33个表。查看jbpm_processdefinition表,会发现有一个名字为pro1,ID为1,version为1的条目。
同样可以查看jbpm_node表。
修改流程pro1的定义,在state1节点添加一个Action,在node-enter事件中。完整的xml文件见该文档最后部分。然后再次运行SimpleDBTest .testDeployProcessDefinition()操作。查看jbpm_processdefinition,会发现有了一个pro1的version为2版本的流程。这里有了一个Jbpm流程版本的概念,可以参考用户手册。
还可查看jbpm_event,jbpm_action表。
添加代码:
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流程的最新版本加载进来,然后实例化。并开始执行,到state1节点停下来(此时Action已经执行过了)。然后把这个实例也存入数据库。
这时候查看jbpm_processInstance表,jbpm_token表。
添加代码
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();
}
}
这段代码将刚才的流程实例从数据库中加载进来,然后执行完毕。
查看表jbpm_processinstance表,会发现上次end字段还是null,现在已经是填写了刚才执行的事件了,表示这个流程实例已经执行完毕。
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>messagemessage>
<message2>message2 message2>
action>
event>
<transition name="" to="end">transition>
state>
<end-state name="end">end-state>
process-definition>
通过上面的分析和实例,我们基本上可以理解Jbpm对数据库的操作。但这还只是一个最开始的实习。如果要理解jbpm的ER模型的话,最好能够把数据库中的表的ER模型做出来。在网上以前看到过相关的图。但是没有说明。如果有朋友有相关的文档的话,麻烦给留个信息,谢谢。