Chapter 4. Persistence
This chapter provides the reader with detailed insight into the Business Process Manager's "persistence" functionality.
Most of the time, the jBPM is used to execute processes that span several transactions. The main purpose of the persistence functionality is to store process executions when wait states occur. It is helpful to think of the process executions as state machines. The intention is to move the process execution state machine from one state to the next within a single transaction.
A process definition can be represented in any of three different forms, namely XML, Java object or a jBPM database record. (Run-time data and log information can also be represented in either of the latter two formats.)
![The Transformations and Different Forms](https://access.redhat.com/webassets/avalon/d/JBoss_Enterprise_SOA_Platform-5-JBPM_Reference_Guide-en-US/images/6431ceea65cef8126aeca1db695f79a0/model.transformations.png)
Figure 4.1. The Transformations and Different Forms
Note
To learn more about XML representations of process definitions and process archives, see Chapter 14, jBPM Process Definition Language .
Note
To learn more about how to deploy a process archive to the database, read Section 14.1.1, “ Deploying a Process Archive ” .
4.1. The Persistence Application Programming Interface
4.1.1. Relationship with the Configuration Framework
The persistence application programming interface is integrated with the configuration framework, (see Chapter 3, Configuration .) This has been achieved by the exposure of some of the
convenience persistence
methods on the JbpmContext
, allowing the jBPM context block
to call persistence API operations.
JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext(); try { // Invoke persistence operations here } finally { jbpmContext.close(); }
4.1.2. Convenience Methods on JbpmContext
The three most commonly performed persistence operations are:
- process. deployment
- new process execution commencement
- process execution continuation
Process deployment is normally undertaken directly from the Graphical Process Designer or from the
deployprocess
ant task. However, to do it directly from Java, use this code:
JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext(); try { ProcessDefinition processDefinition = ...; jbpmContext.deployProcessDefinition(processDefinition); } finally { jbpmContext.close(); }
Create a new process execution by specifying the process definition of which it will be an instance. The most common way to do this is by referring to the name of the process. The jBPM will then find the latest version of that process in the database. Here is some demonstration code:
JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext(); try { String processName = ...; ProcessInstance processInstance = jbpmContext.newProcessInstance(processName); } finally { jbpmContext.close(); }
To continue a process execution, fetch the process instance, the token or the
taskInstance
from the database and invoke some methods on the POJO (Plain Old Java Object) jBPM objects. Afterwards, save the updates made to the processInstance
into the database.
JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext(); try { long processInstanceId = ...; ProcessInstance processInstance = jbpmContext.loadProcessInstance(processInstanceId); processInstance.signal(); jbpmContext.save(processInstance); } finally { jbpmContext.close(); }
Note that it is not necessary to explicitly invoke the
jbpmContext.save
method if the ForUpdate
methods are used in the JbpmContext
class. This is because the save process will run automatically when the jbpmContext
class is closed. For example, one may wish to inform the jBPM that a taskInstance
has completed. This can cause an execution to continue, so the processInstance
related to the taskInstance
must be saved. The most convenient way to do this is by using the loadTaskInstanceForUpdate
method:
JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext(); try { long taskInstanceId = ...; TaskInstance taskInstance = jbpmContext.loadTaskInstanceForUpdate(taskInstanceId); taskInstance.end(); } finally { jbpmContext.close(); }
Important
Read the following explanation to learn how the jBPM manages the persistence feature and uses Hibernate's functionality.
The
JbpmConfiguration
maintains a set of ServiceFactories
. They are configured via the jbpm.cfg.xml
file and instantiated as they are needed.
The
DbPersistenceServiceFactory
is only instantiated the first time that it is needed. After that, ServiceFactory
s are maintained in the JbpmConfiguration
.
A
DbPersistenceServiceFactory
manages a Hibernate ServiceFactory
but this is only instantiated the first time that it is requested.
DbPersistenceServiceFactory parameters:
isTransactionEnabled
sessionFactoryJndiName
dataSourceJndiName
isCurrentSessionEnabled
![The Persistence-Related Classes](https://access.redhat.com/webassets/avalon/d/JBoss_Enterprise_SOA_Platform-5-JBPM_Reference_Guide-en-US/images/9846d01794cb5ff6f5054f967ad9c2ad/persistence.api.png)
Figure 4.2. The Persistence-Related Classes
When the
jbpmConfiguration.createJbpmContext()
class is invoked, only the JbpmContext
is created. No further persistence-related initializations occur at this time. The JbpmContext
manages a DbPersistenceService
class, which is instantiated when it is first requested. The DbPersistenceService
class manages the Hibernate session, which is also only instantiated the first time it is required. (In other words, a Hibernate session will only be opened when the first operation that requires persistence is invoked.)
4.2. Configuring the Persistence Service
4.2.1. The DbPersistenceServiceFactory
The
DbPersistenceServiceFactory
class has three more configuration properties: isTransactionEnabled, sessionFactoryJndiName, and dataSourceJndiName. To specify any of these properties in the jbpm.cfg.xml
file, specify the Service Factory as a bean within the factory element. This sample code demonstrates how to do so:
<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>
Important
Do not mix the short and long notation for configuring the factories. (See also Section 3.1, “ Customizing Factories ”.) If the factory is just a new instance of a class, use the factory attribute to refer to its factory class name but if properties in a factory require configuration, the long notation must be used and, furthermore, the factory and the bean must be combined as nested elements.
isTransactionEnabled
- By default, jBPM will begin a Hibernate transaction when the session is retrieved for the first time and, if the
jbpmContext
is closed, the Hibernate transaction will be ended. The transaction is then committed or rolled back depending on whether or notjbpmContext.setRollbackOnly
was called. (The isRollbackOnly property is maintained in theTxService
.) To disable transactions and prohibit jBPM from managing them with Hibernate, set the isTransactionEnabled property value tofalse
. (This property only controls the behaviour of thejbpmContext
; theDbPersistenceService.beginTransaction()
can still be called directly directly with the application programming interface, which ignores the isTransactionEnabled setting.) To learn more about transactions, please study Section 4.2.2, “ Hibernate Transactions ”. sessionFactoryJndiName
- By default, this is
null
, which means that the session factory will not be fetched from JNDI. If it is set and a session factory is needed in order to create a Hibernate session, it will be fetched from JNDI. dataSourceJndiName
- By default, this is
null
, resulting in creation of JDBC connections being delegated to Hibernate. By specifying a data-source, one makes the Business Process Manager fetch a JDBC connection from the data-source and provide it to Hibernate whilst opening a new session.
4.2.1.1. The Hibernate Session Factory
By default, the
DbPersistenceServiceFactory
uses the hibernate.cfg.xml
file in the root of the classpath to create the Hibernate session factory. Note that the Hibernate configuration file resource is mapped in jbpm.hibernate.cfg.xml. Customise it by reconfiguring 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>
Important
When resource.hibernate.properties is specified, the properties in that resource file will overwrite all of those in
hibernate.cfg.xml
. Instead of updating the hibernate.cfg.xml
to point to the database, use hibernate.properties to handle jBPM upgrades. The hibernate.cfg.xml
file can then be copied without the need to reapply the changes.
4.2.1.2. Configuring a C3PO Connection Pool
Please refer to the Hibernate documentation at http://www.hibernate.org/214.html
4.2.1.3. Configuring an ehCache Provider
To learn how to configure jBPM with JBossCache, read http://wiki.jboss.org/wiki/Wiki.jsp?page=JbpmConfiguration
To learn how to configure a cache provider to work with Hibernate, study http://www.hibernate.org/hib_docs/reference/en/html/performance.html#performance-cache.
The
hibernate.cfg.xml
file that ships with jBPM includes the following line:
<property name="hibernate.cache.provider_class"> org.hibernate.cache.HashtableCacheProvider </property>
This is provided so that users do not have to concern themselves with configuring classpaths.
Warning
Do not use Hibernate's
HashtableCacheProvider
in a production environment.
To use
ehcache
instead of the HashtableCacheProvider
, simply remove the relevant line from the classpath and substitute ehcache.jar
instead. Note that one might have to search for the right ehcache
library version that is compatible with one's environment.
4.2.2. Hibernate Transactions
By default, jBPM delegates transactions to Hibernate by using the "session per transaction" pattern. jBPM will begin a Hibernate transaction when a session is opened the first time when a persistent operation is invoked on the
jbpmContext
. The transaction will be committed right before the Hibernate session is closed. That will happen inside the jbpmContext.close()
.
Use
jbpmContext.setRollbackOnly()
to mark a transaction for rollback. In doing so, the transaction will be rolled back imediately before the session is closed inside the jbpmContext.close()
method.
To prohibit the Business Process Manager from invoking any of the transaction methods via the Hibernate application programming interface, set the isTransactionEnabled property to
false
, as explained in more detail in Section 4.2.1, “The DbPersistenceServiceFactory”.
4.2.3. JTA Transactions
Managed transactions are most commonly found when jBPM is used in the JBoss Application Server. The following code sample shows a common way in which transactions are bound to 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>
Next, configure the Hibernate session factory to use a data-source and bind Hibernate itself to the Transaction Manager. If using more than one datasource, bind them to an
XA datasource
.
<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>
Note
For more information about binding Hibernate to a Transaction Manager, please, refer to http://www.hibernate.org/hib_docs/v3/reference/en/html_single/#configuration-optional-transactionstrategy.
Next, configure Hibernate to use an
XA datasource
.
These configurations allow the enterprise beans to use CMT whilst the web console uses BMT. (This is why jta.UserTransaction is also specified.)
4.2.4. Customizing Queries
All of the SQL queries that jBPM uses are found in one central configuration file. That resource file is referenced in the
hibernate.cfg.xml
configuration file:
<hibernate-configuration> <!-- hql queries and type defs --> <mapping resource="org/jbpm/db/hibernate.queries.hbm.xml" /> </hibernate-configuration>
To customize one or more of those queries, make a back-up of the original file. Next, place the customized version somewhere on the classpath, then update the reference to
org/jbpm/db/hibernate.queries.hbm.xml
in the hibernate.cfg.xml
to point to the customized version.
4.2.5. Database Compatibility
The jBPM runs on any database that is supported by Hibernate.
4.2.5.1. Isolation Level of the JDBC Connection
Set the database isolation level for the JDBC connection to at least
READ_COMMITTED
.
Warning
If it is set to
READ_UNCOMMITTED
, (isolation level zero, the only isolation level supported by Hypersonic), race conditions might occur in the job executor
.These might also appear when synchronization of multiple tokens is occurring.
4.2.5.2. Changing the Database
In order to reconfigure Business Process Manger to use a different database, follow these steps:
- put the JDBC driver library archive in the classpath.
- update the Hibernate configuration used by jBPM.
- create a schema in the new database.
4.2.5.3. The Database Schema
The
jbpm.db
sub-project contains drivers, instructions and scripts to help the user to start using the database of his or her choice. Refer to the readme.html
(found in the root of the jbpm.db
project) for more information.
Note
Whilst the JBPM is capable of generating DDL scripts for any database, these schemas are not always as efficient as they could be. Consider asking your corporation's Database Administrator to review the generated DDL, so that he or she can optimise the column types and indexes.
The following Hibernate configuration option may be of use in a development environment: set hibernate.hbm2ddl.auto to
create-drop
and the schema will be created automatically the first time the database is used in an application. When the application closes down, the schema will be dropped.
4.2.5.3.1. Programmatic database schema operations
jBPM provides an API for creating and droping the database schema through the
org.jbpm.JbpmConfiguration
methods createSchema
and dropSchema
. Be aware that there is no constraint on invoking these methods other than the privileges of the configured database user.
Note
The aforementioned APIs constitute a facade to the broader functionality offered by class
org.jbpm.db.JbpmSchema
:
4.2.5.4. Combining Hibernate Classes
Combining Hibernate and jBPM persistent classes brings about two major benefits. Session, connection and transaction management become easier because, by combining them into one Hibernate session factory, there will be only one Hibernate session and one JDBC connection. Hence, the jBPM updates will be in the same transaction as the updates for the domain model. This eliminates the need for a transaction manager.
Secondly, it enables one to drop one's Hibernate persistence object into the process variables without any additional work.
To make this occur, create one central
hibernate.cfg.xml
file. It is easiest to use the default jBPM hibernate.cfg.xml
as a starting point and add references to one's own Hibernate mapping files to customize it.
4.2.5.5. Customizing the jBPM Hibernate Mapping Files
Follow these steps to customize any of the jBPM Hibernate mapping files:
- copy the jBPM Hibernate mapping files from the sources (
src/jbpm-jpdl-sources.jar
). - place the copy somewhere on the classpath, (ensuring that it is not the same location as they were in previously.
- update the references to the customized mapping files in
hibernate.cfg.xml
4.2.5.6. Second Level Cache
jBPM uses Hibernate's second level cache to keep the process definitions in memory after loading they have been loaded once. The process definition classes and collections are configured in the Hibernate mapping files so that the cache element looks like this:
<cache usage="nonstrict-read-write"/>
Since process definitions will never change, it is acceptable to keep them in the second level cache. (See also Section 14.1.3, “ Changing Deployed Process Definitions ”.)
The default caching strategy is set to
nonstrict-read-write
. During run-time execution, the process definitions remain static, allowing maximum caching to be achieved. In theory, setting the caching strategy read-only
would be even better for run-time execution but, that setting would not permit the deployment of new process definitions.
Having read this chapter, you have learned a great deal of theoretical information and practical advice relating to the topic of persistence in jBPM, including how to utilize Hibernate to its fullest potential.