`
zhangym195
  • 浏览: 121748 次
  • 性别: Icon_minigender_1
  • 来自: 黑龙江
社区版块
存档分类
最新评论

jBPM jPDL 用户开发手册 - 第7章

阅读更多

7章持久化

大多数的情况,jBPM用于维护流程的执行跨度很长时间(long time)。在这个语境下,“很长时间”意味着跨度几个事务。这个持久化的主要意图就是在等待状态期间存储流程执行。所以把流程执行当作一个状态机。在一个事务里,我们移动流程执行状态机从一个状态到另一个状态。
流程定义能够被持久化以3种不同的形式:xml、java对象和jBPM数据库记录。执行(即运行时)信息和日志信息可以表示成2种形式:java对象和jBPM数据库记录。

图 7-1 转换和其不同形式

关于流程定义xml表示和流程包的更多信息,参考第21章 jBPM流程定义语言(JPDL)

更多的关于如部署流程包到数据库的信息可以在21.1.1 部署流程包 节找到。

7.1. 持久化API

7.1.1.配置框架关系

持久化API是一个通过在JbpmContext上暴露一些方便的持久化方法同配置框架进行集成, 持久化API操作可以因此在jBPM上下文块中被调用,像这样:

JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();

try {

  // 调用持久化操作

} finally {

  jbpmContext.close();

}
在下文中,我们假设那个配置包括一个持久化服务跟这个(就像在例子配置文件 src/config.files/jbpm.cfg.xml)相似:
<jbpm-configuration>
 

  <jbpm-context>

    <service name='persistence' factory='org.jbpm.persistence.db.DbPersistenceServiceFactory' />

    ...

  </jbpm-context>

  ...

</jbpm-configuration>
 

7.1.2. JbpmContext上的方便方法

三个最通用的持久操作:

l  部署流程

l  开始一个新的流程执行

l  继续一个执行

首先部署一个流程定义。典型的,这将直接被图形流程设计器或部署流程的ant任务完成。但这里你可以看到程序中如何做的。

JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();

try {

  ProcessDefinition processDefinition = ...;

  jbpmContext.deployProcessDefinition(processDefinition);

} finally {

  jbpmContext.close();

}
为了创建一个新的流程执行,我们需要指定哪一个流程定义上执行将实例化。最常用的方式是指定这个流程的名字并让jBPM在数据库中查找那个流程的最新版本:

JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();

try {

  String processName = ...;

  ProcessInstance processInstance =

      jbpmContext.newProcessInstance(processName);

} finally {

  jbpmContext.close();

}

为了继续一个流程执行,我们需要从数据库中捕获流程实例、token或taskInstance,调用在POJO jBPM对象上的一些方法然后保存这些更新使这个流程实例再次进入数据库中。

JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();

try {

  long processInstanceId = ...;

  ProcessInstance processInstance =

      jbpmContext.loadProcessInstance(processInstanceId);

  processInstance.signal();

  jbpmContext.save(processInstance);

} finally {

  jbpmContext.close();

}

注意如果你用JbpmContext 的xxxForUpdate方法的话,一个明确的jbpmContext.save 方法调用就不再是必须的了,因为在关闭jbpmContext期间它将自动的调用。例如:假设我们想通知jBPM关于taskInstance已经完成了。注意任务实例完成能够触发执行继续所以与taskInstance 相关的processInstance必须被保存。最方便的方式是使用loadTaskInstanceForUpdate方法来做:

JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();

try {

  long taskInstanceId = ...;

  TaskInstance taskInstance =

      jbpmContext.loadTaskInstanceForUpdate(taskInstanceId);

  taskInstance.end();

} finally {

  jbpmContext.close();

}
正如背景信息那样,下部分是一个解释jBPM如何管理持久化和使用hibernate。

JbpmConfiguration维护一系列的服务工厂。这些服务工厂在jbpm.cfg.xml文件中被配置正如上面显示的那样,而且是懒初始化的(instantiated lazy)。DbPersistenceServiceFactory只在它第一次需要的时候被初始化。在那以后,服务工厂被维护在JbpmConfiguration里。DbPersistenceServiceFactory管理hibernate 的SessionFactory。并且当第一次请求hibernate session工厂时它被懒创建(creat lazy)。

图 7-2 持久化关联类

在jbpmConfiguration.createJbpmContext()调用期间,只有JbpmContext被创建。那时没有更多的有关持久化的初始化被做。JbpmContext管理DbPersistenceService,它在第一次请求时被实例化。DbPersistenceService管理hibernate session。而且hibernate session是在DbPersistenceService内部被懒创建。结果,hibernate session将只有当第一个请求持久化的操作被调用且不会早于此请求的时候才被打开。

7.1.3. 管理事件

管理事务的大多数通常的情形是当在JEE应用服务器上(如JBoss)使用jBPM时。大多数通常的情形如下:

l  在你的应用服务器上配置一个DataSource

l  配置hibernate连接用数据源

l  使用窗口管理事务

l  在jBPM中禁止事务

在jBPM前端的无状态的session facade是个好的习惯。最容易的方式如何绑定jbpm事务到容器事务是要确信hibernate配置被jbpm引用的xa-datasource所使用。所以jbpm将使用它自己的hibernate session,那将只被1个jdbc连接和1个事务使用。

jbpm session facade方法的事务属性应该被“要求”。被jbpm使用的指定在hibernate.cfg.xml中的最重要的配置属性是:

hibernate.connection.datasource=  --数据源JNDI 名 — 例如: java:/DefaultDS

更多的关于如何配置hibernate的jdbc连接的信息,参考hibernate参考文档, “Hibernate provided JDBC connections”

更多 的关于如何在jboss中配置xa_datasource的信息,参考jboss应用服务器指导,“Configuring JDBC DataSources”

7.1.4. 注入hibernate session

在某些情况下,你已经有了一个hibernate session并且你要是想合并所有的持久化工作从jBPM进入那个hibernate session中。

那么第一件要做的事情就是确信hibernate配置知道所有的jBPM映射文件。你应该确保引用到src/config.files/hibernate.cfg.xml文件里的所有hibernate映射文件被提供用于hibernate配置。
然后,你能够注入一个hibernate session进入jBPM的上下文就像下列的API片段中显示的那样:

JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();

try {

  jbpmContext.setSession(SessionFactory.getCurrentSession());

  // jbpmContext 上的jBPM操作

} finally {

  jbpmContext.close();

}
传递将在当前的hibernate session中通过容器到jBPM上下文。当一个session被注入到上下文中时没有hibernate事务被初始化。因此这能使用缺省配置。
被传递的hibernate session将不会在jbpmContext.close()方法中被关闭。这行是整个的程序注入的精髓所在,它将下一部分被解释。

7.1.5. 在程序中注入资源

jBPM的配置提供了jBPM创建hibernate session工厂、hibernate session、jdbc连接和jbpm必须服务等等的必要信息。但是所有的这些资源也能够被在程序中提供给jBPM。正好注入他们到jbpmContext中。被注入的资源总是在从jbpm配置信息创建资源之前被接受。

主要的哲学是用户API负责用户在程序中注入jbpmContext的所有事情。另一方面,所有被jBPM打开的项目,将被它关闭。有一个例外,那就是当捕获一个被hibernate创建的连接。当调用jbpmContext.getConnection()时,API用户使用这个中转(transfers)负责从jBPM关闭连接。

JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();

try {

  // 为了在他们被使用之前注入资源,你可以使用

  jbpmContext.setConnection(connection);

  // 或

  jbpmContext.setSession(session);

  // 或

  jbpmContext.setSessionFactory(sessionFactory);

 

} finally {

  jbpmContext.close();

}
 

7.1.6. 高级API 使用

DbPersistenceService维护hibernate session的延迟初始化。所有的数据库访问都是通过这个hibernate session来完成的。所有的查询和更新都是通过jBPM暴露的XxxSession类,像GraphSession、SchedulderSession、LoggingSession等等来完成的。这些session类使用hibernate的查询并且在相同的hibernate session下。

XxxxSession类也是可以通过JbpmContext 进行存取的。

7.2. 配置持久化服务

7.2.1. DbPersistenceServiceFactory

DbPersistenceServiceFactory自己有三个以上的配置属性:isTransactionEnabled、sessionFactoryJndiName和dataSourceJndiName。为了在jbpm.cfg.xml文件中指定属性中的任何一个,你需要像这样把服务工厂指定为factory元素里的一个bean:

重要:不要混用长短标签来配置工厂。参考:6.1 配置工厂。如果工厂只是一个类的新实例,你可以使用工厂的属性去引用工厂元素类名。但是如果工厂属性必须被配置的话,必须使用长标签而且factory和bean必须作为一个嵌套元素来组合使用。像这样:

<jbpm-context>

    <service name="persistence">

      <factory>

        <bean class="org.jbpm.persistence.db.DbPersistenceServiceFactory">

          <field name="isTransactionEnabled"><false /></field>

          <field name="sessionFactoryJndiName">

            <string value="java:/myHibSessFactJndiName" />

          </field>

          <field name="dataSourceJndiName">

            <string value="java:/myDataSourceJndiName" />

          </field>

        </bean>

      </factory>

    </service>

    ...

  </jbpm-context>

l  isTransactionEnabled:缺省的,jBPM将开始一个hibernate事务当第一次捕获session并且如果jbpmContext被关闭时,hibernate事务将被结束。然后事务被提交或者是回滚都依赖于是否jbpmContext.setRollbackOnly被调用。isRollbackOnly属性在TxService中维护。为了禁止并限制jBPM使用hibernate管理事务。配置isTransactionEnabled属性为false像在上面的例子中的那样。这个属性只能控制jbpmContext的行为。你仍然可以直接使用API来调用DbPersistenceService.beginTransaction(),那就可以忽略isTransactionEnabled这个属性设置。更多的关于事务的信息,参考:7.3 Hibernate事务

l  sessionFactoryJndiName:缺省的,这个值是null,意味着session工厂不会被JNDI捕获。如果设置和session工厂是必须的去创建一个hibernate session的话,那么这个session工厂将从jndi中使用提供的JNDI命名被捕获。

l  dataSourceJndiName:缺省的,这个值是null并且hibernate将代理来创建JDBC连接。通过指定数据源,jBPM将从数据源捕获一个JDBC连接并在打开一个新的session时提供给hibernate。为用户提供JDBC连接的内容请参考 ???.(原文没有注明,估计是遍地皆有吧)

7.2.2. hibernate session工厂

缺省情况下,DbPersistenceServiceFactory将使用类路径根下的资源hibernate.cfg.xml文件去创建hibernate的对象工厂。注意hibernate配置文件资源是在jbpm.hibernate.cfg.xml中映射。而且你可以在jbpm.cfg.xml中进行定制。这个缺省的配置是:
<jbpm-configuration>

  ...

  <!-- configuration resource files pointing to default configuration files in jbpm-{version}.jar -->

  <string name='resource.hibernate.cfg.xml'

          value='hibernate.cfg.xml' />

  <!-- <string name='resource.hibernate.properties'

                  value='hibernate.properties' /> -->

  ...

</jbpm-configuration>

当这个属性 resource.hibernate.properties被指定时,资源文件中的属性将重写hibernate.cfg.xml中的所有属性。取代更新hibernate.cfg.xml文件指明数据库,hibernate属性能够方便的用于处理jbpm的升级:复制hibernate.cfg.xml文件就可以而不用非得修改它了。

7.2.3. 配置c3po连接池

请参考hibernate文档: http://www.hibernate.org/214.html

7.2.4. 配置ehcache cache provider

如果你想使用jBossCache来配置jBPM的话,看下jBPM配置wiki页面

更多的关于在hibernate中配置cache provider的信息,去看hibernate文档,二缓存部分

jBPM使用的hibernate.cfg.xml包含下列行:

<property name="hibernate.cache.provider_class">org.hibernate.cache.HashtableCacheProvider</property>

这个被完成将使人们尽可能快地开始和运行而不用担心类路径问题。注意hibernate包含一个声明不要在产品中使用HashtableCacheProvider的警告。
为了使用ehcache代替HashtableCacheProvider,只是移除那行代码并在类路径下放上ehcache.jar就可以。注意你也许还得找下和你的环境兼容的正确ehcache的库版本。前一个在jboss和特定的ehcache版本之间的不兼容的原因是因为改变了缺省的HashtableCacheProvider

7.3. Hibernate事务

缺省情况下,jBPM将委托事务给hibernate并使用每事务模型session。jBPM将在一个hibernate session被打开时开始一个hibernate事务。这个将当持久化操作在jbpmContext上被调用时的第一次发生。这个事务将恰好在hibernate session关闭前被提交。那个将发生在jbpmContext.close()的内部。

使用jbpmContext.setRollbackOnly()来标记事务回滚。那样的话,这个事务将被恰好地回滚到jbpmContext.close()内部关闭session之前的位置。

为了限制jBPM调用hibernate API的事务方法,设置isTransactionEnabled为false,这个在上面的7.2.1 DbPersistenceServiceFactory部分被解释了。

7.4. JTA 事务

大多数情况下为了在一个JEE应用服务器(如JBoss)上使用jBPM时管理事务。为了绑定事务到JTA,使用下面的代码:
<jbpm-context>

    <service name="persistence">

      <factory>

        <bean class="org.jbpm.persistence.db.DbPersistenceServiceFactory">

          <field name="isTransactionEnabled"><false /></field>

          <field name="isCurrentSessionEnabled"><true /></field>

          <field name="sessionFactoryJndiName">

            <string value="java:/myHibSessFactJndiName" />

          </field>

        </bean>

      </factory>

    </service>

    ...

  </jbpm-context>

然后你应该在你的hibernate session工厂中指定使用数据源并且绑定hibernate到事务管理器。确保你绑定数据源到一个XA数据源以防止你使用多个资源。更多关于绑定hibernate到你的事务管理器的信息,请查阅hibernate文档中的“事务策略配置”这一段。

<hibernate-configuration>

  <session-factory>

 

    <!-- hibernate dialect -->

    <property name="hibernate.dialect">org.hibernate.dialect.HSQLDialect</property>

 

    <!-- DataSource properties (begin) -->

    <property name="hibernate.connection.datasource">java:/JbpmDS</property>

 

    <!-- JTA transaction properties (begin) -->

    <property name="hibernate.transaction.factory_class">org.hibernate.transaction.JTATransactionFactory</property>

    <property name="hibernate.transaction.manager_lookup_class">org.hibernate.transaction.JBossTransactionManagerLookup</property>

    <property name="jta.UserTransaction">java:comp/UserTransaction</property>

    ...

  </session-factory>

</hibernate-configuration>
然后确信你已经配置了hibernate使用XA数据源。
这些配置允许EJB使用CTM而且仍然允许web控制台使用BMT。那就为什么'jta.UserTransaction'属性也被指定的原因。

7.5. 定制查询

所有jBPM使用的HQL查询以一个配置文件为中心。那个资源文件被在hibernate.cfg.xm配置文件中像这样被引用:
<hibernate-configuration>
  ...

    <!-- hql queries and type defs -->

    <mapping resource="org/jbpm/db/hibernate.queries.hbm.xml" />

  ...
</hibernate-configuration>
为了定制一个或更多的查询,复制下这个源文件并放你的定制的版本到你的类路径的某处。然后在你的hibernate.cfg.xml文件中更新'org/jbpm/db/hibernate.queries.hbm.xml'引用指向你定制的版本。

7.6. 数据库兼容性

jBPM能运行在任何被hibernate支持的数据库上。在jBPM(src/config.files)下的这个示例配置文件指定了使用hypersonic内存数据库。那个数据库在开发和测试期间是理想的。hypersonic数据库保持所有的数据在内存中而不在硬盘上存储它。

7.6.1. JDBC隔离级

确保为你的JDBC连接配置的数据库隔离级至少是READ_COMMITTED。
即使是READ_UNCOMMITTED(隔离级别0和HSQLDB所支持的隔离级别)几乎所有属性工作正常,但工作执行器和多token同步也许会发生竞争状态(race condition)。

 7.6.2. 变更jBPM数据库

下面是当改变jBPM使用不同的数据库时需要做的指示性列表:

l  放jdbc驱动程序库到类路径上

l  更新jBPM使用的hibernate配置

l  在新数据库中创建模式(schema)

7.6.3. jBPM数据库模式

jbpm.db子项目,包含许多驱动、使用说明和脚本帮助你在你选择的数据库上开始。请查阅jbpm.db项目根下的to the read获得更多的信息。
jBPM能够为所有的数据库生成DDL脚本,这些脚本未必是最优的。所以你也许想让你的DBA评审下所生成的DDL并为其优化列类型及使用索引。

在开发中你可能感兴趣下列的hibernate配置:如果你设置hibernate配置属性'hibernate.hbm2ddl.auto' 为'create-drop'的话(例如在hibernate.cfg.xml中),这个模式将在应用第一次用到它时自动地在数据库中创建。当应用关闭时,这个模式将被丢弃。

模式生成在程序中也可以使用jbpmConfiguration.createSchema()和jbpmConfiguration.dropSchema()进行调用。

7.6.4. 已知问题

本节高亮显示这些已经使用jBPM被测试过的在数据库上使用的已知问题。

7.6.4.1. Sybase 问题

某些sysbase发布包有一个已知的问题截短二进制文件导致系统的不正当的行为。这个限制原因在于sysbase数据库的二进制的存储机制。

7.7.合并hibernate类

在你的项目中,你也许为持久化使用hibernate。将你的持久化类同jBPM的持久化类合并是可选的操作。当将你的hibernate持久化同jBPM's hibernate持久化合并时有两个主要的益处:
首先,session、连接和事务管理变得更容易些。通过合并jBPM和你的持久化到一个hibernate session工厂,就由一个hibernate session、一个jdbc连接处理你和jBPM的持久化。所以当更新到你自己的域模型时在同一个session中jBPM被自动地更新。这就能够消除了使用事务管理的必要。
其次,这样允许你扔你的hibernate持久化对象到流程变量而不会有更多的麻烦(hassle)。
整合你的持久化类同jBPM的持久化类最简易的方式是通过创建一个中心的hibernate.cfg.xml文件。你可以用jBPM的src/config.files/hibernate.cfg.xml作为一个开始点,然后在那里增加些引用到你自己的hibernate映射文件。

7.8. 定制jBPM hibernate映射文件

要定制jBPM hibernate的映射文件,你可以像下面这样处理:

l  从源文件(src/java.jbpm/...)处或jbpm jar包内部段复制jBPM的映射文件。

l  放这个拷贝到你的类路径上的任何地方

l  在你的hibernate.cfg.ml配置文件中更新引用为这些定制的映射文件。

7.9. 二级缓存

一旦加载他们之后jBPM使用hibernate的二级缓存来保持流程定义在内存中。流程定义类和集合在jBPM的hibernate映射文件中被配置使用cache元素像下面这样:

<cache usage="nonstrict-read-write"/>

因为流程定义从不会改变,所以保持它们在二级缓存中是好的。参考21.1.3 改变部署的流程定义

二级缓存是JBoss jBPM实现的一个重要方面。如果它没有这个缓存,JBoss jBPM同其他的实现BPM引擎的技术比较可能会有一系列的缺陷。
缓存的策略是设置非严格读写(nonstrict-read-write)。在运行时,缓存策略可以被设置为只读(read-only)。但是那样的话,你将需要为部署的每个流程设置一个独立的hibernate映射文件。那就是为什么我们选择nonstrict-read-write的原因。
 
:-) chapter 7 has been got!
  • 大小: 15.9 KB
  • 大小: 10.7 KB
分享到:
评论
2 楼 youyoule1225 2009-01-04  
       
1 楼 richmond 2008-09-25  
个人认为是jpbm手册里面较为重要的一章

感谢lz的分享

相关推荐

Global site tag (gtag.js) - Google Analytics