jBPM, JPA, Hibernate, Transactions
After having worked with Oracle BPEL as a tool for business process management for a long time, I got a chance to work on a project with an open source alternative, JBoss jBPM, using its native jPDL language. I immediately liked jBPM. I’ll save a discussion of the benefits of jBPM, and its comparison to Oracle BPEL, for some later time.
For now, I’d like to elaborate on some technical issues that bothered me and my colleagues for days before we figured them out completely. It had to do with jBPM, Hibernate and transactions. According to Google, many people are having trouble with that, but there aren’t a lot of good answers.
The setup
In our project, we’re using jBPM 3.3.1.GA. I didn’t want to use a 4.x version, because in my view, there are too many things in 4.x still unsupported and unclear.
We’re using jBPM in embedded mode, as a library. While it is possible to run jBPM as a server (BPEL style), this would eliminate some important benefits, in particular testability. We’re testing all of our code (jBPM processes, web services, plain Java classes) with regular JUnit tests, using OpenEJB as a lightweight application server and HSQLDB as an in-memory database.
Persistence
If you’re creating a meaningful business process in jBPM, chances are you will also need to store the values of process variables. jBPM can easily store primitive values with a business process, and also anything that’s serializable.
However, persisting objects in their serialized form has a severe drawback: from a database perspective, they’re simply random binary data. No meaning, no querying, no relational integrity. It would be much more useful to have jBPM store process variables using true object-relational mapping. jBPM supports this model as well.
When writing any Java EE application, JPA is the obvious choice for implementing object-relational mapping. Hibernate is a widely-used implementation. jBPM also uses Hibernate for its persistence logic. So, a rather easy way of integration comes to mind: configure JPA with Hibernate as the implementation, and have jBPM communicate with the Hibernate core that underlies the Hibernate JPA implementation. A reference to the native Hibernate SessionFactory can easily be obtained by downcasting an injected EntityManagerFactory to a HibernateEntityManagerFactory. Simply inject this into the jbpmContext and you’re good to go.
This Does Not Work.
Transactions in Hibernate JPA and the native API
The key point here is that the Hibernate JPA implementation (called Hibernate EntityManager) is not an add-on to the native API. It is an alternative. The two can’t be combined, and transaction management is the area where the problem surfaces. Hibernate uses its own abstraction of a transaction (org.hibernate.Transaction). This allows Hibernate to work with either local JDBC transactions or JTA transactions. Hibernate obtains a transaction implementation using a TransactionFactory.The factory can be configured using the property hibernate.transaction.factory_class.
The Hibernate native API expects the transaction.factory_class property to be set. By configuring the CMTTransactionFactory or the JTATransactionFactory, either the JTA TransactionManager (container managed) or the JTA UserTransaction (user managed) can be used. Contrarily, the Hibernate JPA interface does not expect transaction.factory_class to be set, and will give a warning if it is. It will by itself choose the JoinableCMTTransactionFactory.
Now here is the problem: if you use Hibernate JPA without setting transaction.factory_class, regular EntityManager operations work well, but if you inject the SessionFactory into jBPM, jBPM will run into transaction errors. If you set transaction.factory_class, jBPM works fine, but regular EntityManager operations fail.
This simply means that, when integrating with jBPM, you’re forced to abandon JPA in favour of the Hibernate native API.
Do I really have to use these old school mapping files?
After discovering this, one of my initial fears was that this also meant I had to start using Hibernate XML mapping files instead of JPA annotations. This is not the case. Hibernate Annotations work fine in combination with the Hibernate native API.
A somewhat tricky issue is cascading. A standard @..To..(cascade = {CascadeType.PERSIST}) doesn’t work, because jBPM will use the native API, and will not “persist” anything. It will “save” the entities, and the save operation is not cascaded by this annotation. This can be fixed by using the Hibernate-specific cascade annotations in addition to the standard JPA annotations.
A final caveat: Running unit tests with HSQLDB
When running unit tests with an in-memory database, the schema needs to be exported at every run. We noticed that the regular hibernate.hbm2ddl.auto=create option doesn’t work in this setup. The problem was that Hibernate tries to set auto-commit as part of the schema export, but the schema export itself takes place in a transaction. We worked around this problem by explicitly calling org.hibernate.tool.hbm2ddl.SchemaExport on our JDBC connection as part of our test setup, rather than relying on hibernate.hbm2ddl.auto.
Thanks Frans, This is good feedback that we’ll certainly take into account in our new initiative. ( http://processdevelopments.blogspot.com/2010/03/alive-and-kicking.html )
regards, tom.