Why I finally ditched Hibernate native APIs for JPA

If you're looking for the short answer, go check out the Spring Data JPA project.  This is an incredible product that offers a tremendous productivity boost for projects using JPA.  If you're interested in the more gory details, read on below :)

I've been a Hibernate user since 2005 and have used JPA + Hibernate Annotations since 2006 when 1.0 of the JPA spec was released.  Unlike many others I did not immediately jump to the JPA APIs (EntityManager, PersistenceContext, etc) and continued using the native Hibernate APIs (Session, SessionFactory, etc).  JPA was still missing quite a few useful features such as a Criteria API and I wasn't ready to give that up just to use a "standard API."  When JPA 2.0 was released in late 2009 the feature sets of the two products were generally comparable making the decision a little tougher.  But since I've never really bought into the "vendor portability" promise of JPA, I continued happily on with native Hibernate APIs to much success.

Over the past month or two, I've come to the decision it's time to fully embrace JPA.

JPA has grown beyond its original purpose as an object-relational mapping framework into a more generic persistence API.  NoSQL / data grid solutions have become incredibly important and popular over the past few years.  Several JPA-based implemenations for these solutions have already been developed, including Google App Engine / Big Table and Hibernate's own Object/Grid Mapper (OGM).  I started to experiment with GAE about a month ago and was surprised how quickly I could be productive with its JPA implementation.  While JPA likely isn't the best fit for the diverse range of NoSQL implementations out there, the ease of use for JPA developers is undeniable.

While industry trends are important, I've finally found the killer app for JPA: the Spring Data JPA project.  At its core, SDJ is about generating JPQL at runtime so you don't have to write tedious queries.  Some of the awesome features include:

  • Out-of-the-box support for data pagination and sorting.
  • Query creation from method names.  Creating a method signature of findByEmailAddressAndLastName(String emailAddress, String lastName) creating a backing query that does exactly what you'd expect.
  • Specification API to define and combine predicates in a manner similar to the Criteria API.

Check out the reference documentation for Spring Data JPA.  The project just dropped its first 1.0 release candidate.  I'm excited to see what they'll come up with in future releases.

Mixing and Matching Spring JdbcTemplate and HibernateTemplate

The JdbcTemplate and HibernateTemplate convenience classes from Spring really make working with the respective APIs a breeze. Unfortunately getting both of these classes to work together within a single Transaction is not straightforward. This comes up very frequently in JUnit tests where you want to verify Hibernate is working with the database in the way you expect, either by inserting data and letting Hibernate load it or by checking to see that Hibernate creates the data you expect. The same will hold true in application code where you need to add JDBC code alongside Hibernate code to meet various requirements. The testing scenarios are simple and illustrative so let's explore those.

One common use case is to persist an object with HibernateTemplate and then verify the data was inserted correctly using JdbcTemplate. Usually Hibernate will not flush the data out to the DB until the transaction commits, meaning that the query done by JdbcTemplate won't be able to see the new data. This one isn't hard to work around: just call HibernateTemplate.flush() to execute the SQL on demand so that subsequent calls to JdbcTemplate will see the new data.

The second use case is a lot tricker: let's say you want to create some data with JdbcTemplate and then make sure that calls to HibernateTemplate will see that data. By default this will not work. You can actually insert with JdbcTemplate, make a call to load the data with HibernateTemplate (it won't find it) and then make another call to JdbcTemplate which will show that the data is there. The problem is that since JdbcTemplate is injected with a DataSource it doesn't really have any knowledge of the transactions from HibernateTransactionManager; thus operations from the two templates are isolated from one another.

Fortunately Spring offers a solution in the TransactionAwareDataSourceProxy class. Just like the name imples, this class acts as a wrapper for an existing DataSource so that all collaborators will participate in Spring-managed transactions. Configuration of this class is trivial:

<bean id="dataSource" class="org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy">
<property name="targetDataSource">
<bean class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
...
</bean>
</property>
</bean>

Note: you may or may not want to define the "real" DataSource as an inner bean that doesn't get registered in the ApplicationContext itself. If you are autowiring your DataSource purely by type, having two different implementations of DataSource will be a problem for you. Workarounds include autowiring using @Qualifier or using @Resource to inject the bean by name.

On Hibernate, Spring, Sessions and Transactions

I was recently working with Spring and Hibernate on a pet project and ran into some issues with Session and Transaction management that proved to be pretty interesting in the end. The following assumes a working knowledge of Hibernate and Spring...

I was in the midst of writing some JUnit 4.x tests using SpringJUnit4ClassRunner and the @TransactionalConfiguration / @Transactional annotations for automatic rollback of @Test methods. I wanted to do some manipulation of the database prior to my tests using a separate method annotated with @Before. What I was reminded of very quickly is that Spring's class runner will not apply a transactional aspect to this method since it's not actually a @Test. This isn't a problem if you are using HibernateTemplate / HibernateCallback, since it ultimately has a reference back to your TransactionManager to handle transactions. But if you want to work with the raw Hibernate APIs it can be problematic.

There are two things to keep in mind: (1) SessionFactory.getCurrentSession() will only work if you have configured the SessionFactory appropriately, and (2) depending on the configuration, you may have to manage Transactions explicitly. The configuration property in question is "hibernate.current_session_context_class" and it is commonly configured one of three different ways:

1. Omitted the property from the configuration

Hibernate will throw an exception on calls to getCurrentSession() complaining that there is no CurrentSessionContext configured.

2. Configured with 'thread'

hibernate.current_session_context_class=thread

Hibernate will bind the Session returned from getCurrentSession() to the current thread and you must manage transactions programmatically. Generally all that's required is to call Session.beginTransaction(). You can also invoke Transaction.commit() or rollback() if you wish.

3. Configured with SpringSessionContext

hibernate.current_session_context_class=org.springframework.orm.hibernate3.SpringSessionContext

Hibernate will assume it is executing inside of a Spring transactional context (i.e. through a Spring transactional aspect) and Spring will now manage your transaction for you. However if you call getCurrentSession() outside of such a context, Hibernate will throw an exception complaining that no Session is bound to the thread.

What does all this mean?

  1. Use SpringSessionContext if your operations will be done through classes that are invoked through a Spring-managed transactional context or if you can introduce HibernateTemplate and/or HibernateCallback wherever you need it.
  2. Use "thread" if you need to raw with the raw Hibernate Session/Transaction API and remember that you'll need to manage transactions programmatically.