jBPM5.4流程持久化的最简单配置实例

felix_alone2012 2013-09-01

学习jBPM工作流,最鼓舞人心的就是与数据库的交互了,就是把流程运行时状态持久化到数据库中。这一块,在jBPM3.1.2中已经用的很习惯了(其实也就是配置下持久化环境,这个公司项目前辈已经搭建好了,开发就直接调service就OK了),现在一下子跳到jBPM5.4版本了,要尝试自己来配置环境,实在是饭馆里洗碗生亲自下厨啊,尴尬,也没什么啦,只要有菜谱,做的还是八九不离十的嘛。我们就参考官方文档来学习吧。为了便于大家理解,还是翻译成中文吧(英语其实很菜的):

 

本段技术资料摘自于:http://docs.jboss.org/jbpm/v5.4/userguide/,请不要用于任何商业用途!!

 

8.1.3.2.  使用JBPMHelper来配置引擎去使用持久化

要配置jBPM引擎来使用持久化,通常在创建session的时候可以通过使用适当的构造器。有很多方式来创建session(为了尽可能的为你简化提供了几个工具类给你,在你编写一个流程单元测试的时候可以用到)。

最简单的方式是使用jbpm-test模块来轻松创建和测试流程。JBPMHelper类有个创建session的方法,并使用一个配置文件来配置session,配置包括是否想使用持久化、数据源等等。这个帮助类接下来会为你做所有的安装和配置。

要配置持久化,要创建一个jBPM.properties文件并配置下面的属性(注意下面给的例子是默认的配置,使用H2内存数据库来启用持久化,如果你感觉这些配置很好了,就不要再添加新的配置文件,默认就用到这些属性)。

# 用于创建数据源
persistence.datasource.name=jdbc/jbpm-ds
persistence.datasource.user=sa
persistence.datasource.password=
persistence.datasource.url=jdbc:h2:tcp://localhost/~/jbpm-db
persistence.datasource.driverClassName=org.h2.Driver

# 用于配置session持久化
persistence.enabled=true
persistence.persistenceunit.name=org.jbpm.persistence.jpa
persistence.persistenceunit.dialect=org.hibernate.dialect.H2Dialect

# 用于配置人工任务服务
taskservice.enabled=true
taskservice.datasource.name=org.jbpm.task
taskservice.transport=mina
taskservice.usergroupcallback=org.jbpm.task.service.DefaultUserGroupCallbackImpl
    

如果你要使用持久化,你必须确保数据源(在jBPM.properties文件中指定的)正确初始化。这意味着数据库本身要启动并运行,并且数据库应该使用正确的名称来注册。如果你想要使用H2内存数据库(通常最易于做一些测试),你可以使用JBPMHelper类来启动它,使用下面语法:

JBPMHelper.startH2Server();

    

要注册数据源(这是你总要做的,尽管你不使用H2作为你的数据库,检查下面更多的选项来配置你的数据库),使用:

JBPMHelper.setupDataSource();

    

接下来,你可以使用JBPMHelper类来创建session(先要创建知识库,这个和不使用持久化的情况是一样的):

StatefulKnowledgeSession ksession = JBPMHelper.newStatefulKnowledgeSession(kbase);

    

这些都做好了,你只要调用这个ksession上的方法(如startProcess)然后引擎会在创建好的数据源中持久化所有的运行时状态。

你也可以使用JBPMHelper类来重新创建session(通过从数据库恢复它的状态,只需要传递session id(id可使用ksession.getId()方法获取到)):

StatefulKnowledgeSession ksession = 

    JBPMHelper.loadStatefulKnowledgeSession(kbase, sessionId);

    

8.1.3.3. 手动配置引擎来使用持久化

你也可以使用JPAKnowledgeService来创建知识会话。这稍微复杂些,但会让你访问到所有的优先配置。你可以使用基于知识库、知识会话配置(如果必要)和环境的JPAKnowledgeService来创建一个新的知识会话。环境需要包含对你的实体管理器工厂的引用,例如:

// 创建实体管理器工厂并在环境中注册它

EntityManagerFactory emf =

    Persistence.createEntityManagerFactory( "org.jbpm.persistence.jpa" );

Environment env = KnowledgeBaseFactory.newEnvironment();

env.set( EnvironmentName.ENTITY_MANAGER_FACTORY, emf );


// 创建一个新的知识会话来使用JPA存储运行时状态

StatefulKnowledgeSession ksession =

    JPAKnowledgeService.newStatefulKnowledgeSession( kbase, null, env );

int sessionId = ksession.getId();


// 这儿调用你自己的方法

ksession.startProcess( "MyProcess" );

ksession.dispose();

你也可以使用JPAKnowledgeService来根据一个指定的session id重新创建session:

// 使用sessionId从数据库恢复session

ksession = JPAKnowledgeService.loadStatefulKnowledgeSession(

    sessionId, kbase, null, env );

你需要添加持久化配置到你的类路径下,以便配置JPA使用Hibernate和H2数据库(或其他数据库),要创建一个META-INF目录来存放一个persistence.xml文件,显示如下。更多关于如何修改这些配置,可以从JPA和Hibernate文档中获取更多信息。

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<persistence
  version="1.0"
  xsi:schemaLocation=
    "http://java.sun.com/xml/ns/persistence
     http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd
     http://java.sun.com/xml/ns/persistence/orm
     http://java.sun.com/xml/ns/persistence/orm_1_0.xsd"
  xmlns:orm="http://java.sun.com/xml/ns/persistence/orm"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns="http://java.sun.com/xml/ns/persistence">

  <persistence-unit name="org.jbpm.persistence.jpa" transaction-type="JTA">
    <provider>org.hibernate.ejb.HibernatePersistence</provider>
    <jta-data-source>jdbc/jbpm-ds</jta-data-source>
    <mapping-file>META-INF/JBPMorm.xml</mapping-file>
    <class>org.drools.persistence.info.SessionInfo</class>
    <class>org.jbpm.persistence.processinstance.ProcessInstanceInfo</class>
    <class>org.drools.persistence.info.WorkItemInfo</class>

    <properties>
      <property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/>
      <property name="hibernate.max_fetch_depth" value="3"/>
      <property name="hibernate.hbm2ddl.auto" value="update"/>
      <property name="hibernate.show_sql" value="true"/>
      <property name="hibernate.transaction.manager_lookup_class"
                value="org.hibernate.transaction.BTMTransactionManagerLookup"/>
    </properties>
  </persistence-unit>
</persistence>

这个配置文件指向的数据源叫做“jbdc/jbpm-ds”。如果在你的应用服务器中运行应用(例如JBoss AS),这些容器可以使用自己的一些配置来简单建立数据源(例如在部署目录中放一个数据源配置文件)。可以参考你的应用服务器的文档来获取如何配置这些。

例如,如果你部署应用到JBoss 应用服务器 v5.x中,你可以在部署目录中丢进一个配置文件来创建数据源,如:

<?xml version="1.0" encoding="UTF-8"?>
<datasources>
  <local-tx-datasource>
    <jndi-name>jdbc/jbpm-ds</jndi-name>
    <connection-url>jdbc:h2:tcp://localhost/~/test</connection-url>
    <driver-class>org.h2.jdbcx.JdbcDataSource</driver-class>
    <user-name>sa</user-name>
    <password></password>
  </local-tx-datasource>
</datasources>

如果你在Java环境中执行的话,你可以使用JBPMHelper类来做这个(如上)或者可用下面的代码段来建立数据源(这儿我们把Bitronix和H2内存数据库结合使用了)。

PoolingDataSource ds = new PoolingDataSource();

ds.setUniqueName("jdbc/jbpm-ds");

ds.setClassName("bitronix.tm.resource.jdbc.lrc.LrcXADataSource");

ds.setMaxPoolSize(3);

ds.setAllowLocalTransactions(true);

ds.getDriverProperties().put("user", "sa");

ds.getDriverProperties().put("password", "sasa");

ds.getDriverProperties().put("URL", "jdbc:h2:tcp://localhost/~/jbpm-db");

ds.getDriverProperties().put("driverClassName", "org.h2.Driver");

ds.init();

 

上面的配置就是建立持久化配置,在流程执行的时候可以把状态保存到数据库中,并在需要的时候再从数据库恢复状态继续执行。大体就这么理解吧。

 

好啦,我们就参照上面的这段文档说明,自己来建一个实例试一试吧。

 

1.    启动Eclipse,建一个Java Project,先准备基本的环境:

       项目基本结构如下:

       Test

             src

                   com.test

                           Test01.java

                           hello.bpmn

                           jBPM.properties

              JRE...

             jBPM5.4

             junit

          

2.     Test01.java

public class Test01 {
	@Test
	public void test() {
		try {
			// 读取流程定义,获取知识库对象
			KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
			kbuilder.add(ResourceFactory.newClassPathResource("com/test/hello.bpmn"), ResourceType.BPMN2);
			KnowledgeBase kbase = kbuilder.newKnowledgeBase();
			
			// 启动h2数据库
			org.h2.tools.Server server = JBPMHelper.startH2Server();
			
			
			//JBPMHelper.startUp();
			// 建立数据源
			bitronix.tm.resource.jdbc.PoolingDataSource s = JBPMHelper.setupDataSource();
			//System.out.println(s);
			// 创建有状态知识会话
			StatefulKnowledgeSession ksession = JBPMHelper.newStatefulKnowledgeSession(kbase);
			
			int id = ksession.getId();
			
			System.out.println(id);
			// 开始流程执行,会进行持久化
			ksession.startProcess("com.sample.bpmn");
			// 从数据库加载流程
			StatefulKnowledgeSession ksession2 = JBPMHelper.loadStatefulKnowledgeSession(kbase, id);
			
			System.out.println(ksession2);
			
			
			
			
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

}

 3. 运行说明

     这个程序中的流程我们设计的很简单,就一个开始节点、一个结束节点和中间的一个脚本任务节点。

     关键是配置持久化,这里面我们使用了jBPM提供的一个帮助类JBPMHelper来在测试中进行持久化,

   这个就很简单了,我们可以使用默认的持久化配置,也就数这里的jBPM.properties文件,我们其实没有必要覆盖默认定义的(在相应的jar包里可以找到这个定义文件),数据源配置都是默认的,很简单的。

 

 

 

如果我们想要手动进行持久化的配置,我们想使用自己额数据库、想使用服务器提供的数据源,这些都可以参照上面的文档来实现,这里我们还是基于jbpm-test,做一个最简单的测试,还是使用JBPMhelper来启动H2数据库并建立数据源,但是这里我们就不能使用它默认加载的jBPM.properties文件了,我们要在类路径下建一个META-INF目录,里面放一个persistence.xml文件,这个文件很简单:

 

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

<persistence

  version="1.0"

  xsi:schemaLocation=

    "http://java.sun.com/xml/ns/persistence

     http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd

     http://java.sun.com/xml/ns/persistence/orm

     http://java.sun.com/xml/ns/persistence/orm_1_0.xsd"

  xmlns:orm="http://java.sun.com/xml/ns/persistence/orm"

  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

  xmlns="http://java.sun.com/xml/ns/persistence">



  <persistence-unit name="org.jbpm.persistence.jpa" transaction-type="JTA">

    <provider>org.hibernate.ejb.HibernatePersistence</provider>

    <jta-data-source>jdbc/jbpm-ds</jta-data-source>

    <mapping-file>META-INF/JBPMorm.xml</mapping-file>

    <class>org.drools.persistence.info.SessionInfo</class>

    <class>org.jbpm.persistence.processinstance.ProcessInstanceInfo</class>

    <class>org.drools.persistence.info.WorkItemInfo</class>



    <properties>

      <property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/>

      <property name="hibernate.max_fetch_depth" value="3"/>

      <property name="hibernate.hbm2ddl.auto" value="update"/>

      <property name="hibernate.show_sql" value="true"/>

      <property name="hibernate.transaction.manager_lookup_class"

                value="org.hibernate.transaction.BTMTransactionManagerLookup"/>

    </properties>

  </persistence-unit>

</persistence>

 这个配置我们再熟悉不过了,持久化配置都有了,这里还会加载一个ProcessInstanceInfo实例,这个就是流程实例持久化对象,当然了因为使用的是Hibernate操作,肯定要提供query模块了还有mapping配置,这些在jBPM持久化的相关jar包中都有了,包括JBPMorm.xml和ProcessInstanceInfo.hbm.xml,我们可以使用默认的配置,没有必要添加进来了。

JBPMorm.xml:

<?xml version="1.0" encoding="UTF-8"?>
<entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm"
                 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                 xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm orm_1_0.xsd"
                 version="1.0">
      <named-query name="ProcessInstancesWaitingForEvent">
          <query>
select 
    processInstanceInfo.processInstanceId
from 
    org.jbpm.persistence.processinstance.ProcessInstanceInfo processInstanceInfo join processInstanceInfo.eventTypes eventTypes
where
    eventTypes = :type
          </query>
      </named-query>
      
</entity-mappings>

ProcessinstanceInfo.hbm.xml:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >
<hibernate-mapping package="org.jbpm.persistence.processinstance">

  <!-- access="field" for fields that have no setter methods -->
  <class name="ProcessInstanceInfo" table="ProcessInstanceInfo">

    <id name="processInstanceId" type="long" column="InstanceId">
      <generator class="native" />
    </id>

    <version name="version" type="integer" unsaved-value="null" access="field">
      <column name="OPTLOCK" not-null="false" />
    </version>

    <property name="processId" access="field" />
    <property name="startDate" type="timestamp" access="field" />
    <property name="lastReadDate" type="timestamp" access="field" />
    <property name="lastModificationDate" type="timestamp" access="field" />
    <property name="state" type="integer" not-null="true" access="field" />

        <property name="processInstanceByteArray" type="org.hibernate.type.PrimitiveByteArrayBlobType" 
            column="processInstanceByteArray" access="field" length="2147483647" />

    <set name="eventTypes" table="EventTypes" access="field">
      <key column="InstanceId" />
      <element column="element" type="string" />
    </set>

    <!-- NOT mapping [processInstance] field because field is transient -->
    <!-- NOT mapping [env] field because field is transient -->

  </class>

</hibernate-mapping>

 

    注意了!!上述的两个文件我在未手动添加到项目classpath的时候执行流程报错了:

 

   

a PoolingDataSource containing an XAPool of resource jdbc/jbpm-ds with 0 connection(s) (0 still available)
0    01/09 10:34:25,158[main] ERROR hibernate.impl.SessionFactoryImpl.<init>  - Error in named query: ProcessInstancesWaitingForEvent
org.hibernate.hql.ast.QuerySyntaxException: ProcessInstanceInfo is not mapped [select processInstanceInfo.processInstanceId from ProcessInstanceInfo processInstanceInfo join processInstanceInfo.eventTypes eventTypes where eventTypes = :type]
	at org.hibernate.hql.ast.util.SessionFactoryHelper.requireClassPersister(SessionFactoryHelper.java:181)
	at org.hibernate.hql.ast.tree.FromElementFactory.addFromElement(FromElementFactory.java:110)
	at org.hibernate.hql.ast.tree.FromClause.addFromElement(FromClause.java:94)
	at org.hibernate.hql.ast.HqlSqlWalker.createFromElement(HqlSqlWalker.java:316)
	at org.hibernate.hql.antlr.HqlSqlBaseWalker.fromElement(HqlSqlBaseWalker.java:3228)
	at org.hibernate.hql.antlr.HqlSqlBaseWalker.fromElementList(HqlSqlBaseWalker.java:3112)
	at org.hibernate.hql.antlr.HqlSqlBaseWalker.fromClause(HqlSqlBaseWalker.java:720)
	at org.hibernate.hql.antlr.HqlSqlBaseWalker.query(HqlSqlBaseWalker.java:571)
	at org.hibernate.hql.antlr.HqlSqlBaseWalker.selectStatement(HqlSqlBaseWalker.java:288)
	at org.hibernate.hql.antlr.HqlSqlBaseWalker.statement(HqlSqlBaseWalker.java:231)
	at org.hibernate.hql.ast.QueryTranslatorImpl.analyze(QueryTranslatorImpl.java:254)
	at org.hibernate.hql.ast.QueryTranslatorImpl.doCompile(QueryTranslatorImpl.java:185)
	at org.hibernate.hql.ast.QueryTranslatorImpl.compile(QueryTranslatorImpl.java:136)
	at org.hibernate.engine.query.HQLQueryPlan.<init>(HQLQueryPlan.java:101)
	at org.hibernate.engine.query.HQLQueryPlan.<init>(HQLQueryPlan.java:80)
	at org.hibernate.engine.query.QueryPlanCache.getHQLQueryPlan(QueryPlanCache.java:94)
	at org.hibernate.impl.SessionFactoryImpl.checkNamedQueries(SessionFactoryImpl.java:484)
	at org.hibernate.impl.SessionFactoryImpl.<init>(SessionFactoryImpl.java:394)
	at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1341)
	at org.hibernate.cfg.AnnotationConfiguration.buildSessionFactory(AnnotationConfiguration.java:867)
	at org.hibernate.ejb.Ejb3Configuration.buildEntityManagerFactory(Ejb3Configuration.java:669)
	at org.hibernate.ejb.HibernatePersistence.createEntityManagerFactory(HibernatePersistence.java:126)
	at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:52)
	at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:34)
	at com.test.Test01.test(Test01.java:41)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:601)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:45)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:42)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:263)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:68)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:47)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:222)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:300)
	at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
	at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)

    

   后来自己研究发现:在JBPMorm.xml文件中的查询语句要把ProcessInstanceInfo指定为完全限定类名!!我们再把这两个文件添加到classpath中,运行就OK了,酷

 

    

a PoolingDataSource containing an XAPool of resource jdbc/jbpm-ds with 0 connection(s) (0 still available)
Hibernate: insert into SessionInfo (id, lastModificationDate, rulesByteArray, startDate, OPTLOCK) values (null, ?, ?, ?, ?)
36
Hibernate: insert into ProcessInstanceInfo (InstanceId, OPTLOCK, processId, startDate, lastReadDate, lastModificationDate, state, processInstanceByteArray) values (null, ?, ?, ?, ?, ?, ?, ?)
Hibernate: select processins0_.InstanceId as col_0_0_ from ProcessInstanceInfo processins0_ inner join EventTypes eventtypes1_ on processins0_.InstanceId=eventtypes1_.InstanceId where eventtypes1_.element=?
Hibernate: update SessionInfo set lastModificationDate=?, rulesByteArray=?, startDate=?, OPTLOCK=? where id=? and OPTLOCK=?
Hibernate: delete from ProcessInstanceInfo where InstanceId=? and OPTLOCK=?
Hibernate: select sessioninf0_.id as id2_0_, sessioninf0_.lastModificationDate as lastModi2_2_0_, sessioninf0_.rulesByteArray as rulesByt3_2_0_, sessioninf0_.startDate as startDate2_0_, sessioninf0_.OPTLOCK as OPTLOCK2_0_ from SessionInfo sessioninf0_ where sessioninf0_.id=?
org.drools.command.impl.CommandBasedStatefulKnowledgeSession@3730ebf6

 

 

 

最后的测试程序是:

 

    

public class Test01 {

	@Test
	public void test() {
		
		org.h2.tools.Server server = JBPMHelper.startH2Server();
		
		bitronix.tm.resource.jdbc.PoolingDataSource s = JBPMHelper.setupDataSource();
		System.out.println(s);

		KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
		kbuilder.add(ResourceFactory.newClassPathResource("com/test/hello.bpmn"), ResourceType.BPMN2);
		KnowledgeBase kbase = kbuilder.newKnowledgeBase();
		
		
		
		// 创建实体管理器工厂并在环境中注册它

		EntityManagerFactory emf =

		    Persistence.createEntityManagerFactory( "org.jbpm.persistence.jpa" );

		Environment env = KnowledgeBaseFactory.newEnvironment();

		env.set( EnvironmentName.ENTITY_MANAGER_FACTORY, emf );


		// 创建一个新的知识会话来使用JPA存储运行时状态

		StatefulKnowledgeSession ksession =

		    JPAKnowledgeService.newStatefulKnowledgeSession( kbase, null, env );

		int sessionId = ksession.getId();
		
		System.out.println(sessionId);
		
		


		// 这儿调用你自己的方法

		ksession.startProcess( "com.sample.bpmn" );
		
		

		ksession.dispose();
		
		StatefulKnowledgeSession ksession2 = JPAKnowledgeService.loadStatefulKnowledgeSession(sessionId, kbase, null, env);
		
		System.out.println(ksession2);
	}

}

 

       也是比较简单的,后面我们再在持久化配置的基础上介绍如何进行事务的配置吧,尴尬

   

 

beowulf1985sr 2013-09-09
人参萝卜 2013-09-12
分享的不错,赞一个!
Global site tag (gtag.js) - Google Analytics