Thursday, June 28, 2007

How to close a JPA EntityManger in web applications

When working with resources it's always important to ensure they are closed when not longer needed.
Working with JPA there are two kind of resources we can take care of: EntityManager and transactions.

Context

As of Java EE, you can use IoC to inject an EntityManager so the Container is the one who manage the whole life cycle of EntityManagers.
The context we are referring here is when no such a IoC exists and you need to create/destroy EntityManagers each time you use it in a business method. So, if this is your scenario, you can take an approach similar to those described below:

  • Non enterprise applications (i. e. Servlet Container), and therefore no injection of EntityManagers.
  • Method scope for EntityManager.

Approaches

Because we are creating an EntityManager inside a business method who uses the Java Persistence API, we'll assume the existence of an attribute emf which actually is the unique and global instance of EntityManagerFactory needed within an application.

Easy but not enough approach

At first sight, you can think of creating and closing the EntityManager as follows:
public Customer getBestCustomerOfMonth() {

EntityManagerFactory emf = ... ;
EntityManager em = emf.createEntityManager();
// business logic
em.close();
}
However, as you can guess, this code doesn't ensure the EntityManager is always closed when the method finishes.
As far as a RuntimeException occurs in the business logic, the em EntityManager remains open!
You'll always want to avoid this sort of code.

Second approach

You can nest the line for closing the EntityManager inside a finally block, so you can re-write the latter snippet of code as:
public Customer getBestCustomerOfMonth() {

EntityManagerFactory emf = ... ;
EntityManager em = emf.createEntityManager();
try {
// business logic
} finally {
em.close();
}
}
Do you think this is enough to dispose all the EntityManger's resources?
Well, actually this is the case when no transaction is involved. Note that the getBestCustomerOfMonth() method doesn't create any transaction from the EntityManager.

In presence of transactions

If you need transactions, you'll have to create and close them explicitly (as far as you don't use an enterprise application server) in a similar way you have already done with the EntityManager.

At OTN, there is a simple tutorial showing how to use JPA that use the previous approach in presence of transactions!

This is a snippet of code extracted from the mentioned OTN tutorial:
public void alterOrderQuantity(long orderId, int newQuantity) {

EntityManager em = jpaResourceBean.getEMF().createEntityManager();
try{
em.getTransaction().begin();
Order order = em.find(Order.class, orderId);
order.setQuantity(newQuantity);
em.getTransaction().commit();
}finally{
em.close();
}
}
However the previous approach is not enough. This is not completely right because as it is said in the JPA javadocs when the EntityManager.close() method is invoked, the Persistent Context associated with this EntityManager remains managed till the underlying transaction completes. So What happens to the EntityManager instance if an exception occurs before closing the transaction?

Although the close() method of the EntityManager is invoked (finally clause), its Persistence Context will remain managed for the transaction hasn't commits!

Final approach

So, when using transactions outside an enterprise application server because you'll have to close (commit or rollback) the transaction in the same way you do for EntityMangers.
In order for these resources (both EntityManager and underlying transaction) to be closed you'll need to make an additional level of nesting and write your code similar to this one:
public Customer updateCustomer(Customer cust) {

EntityManagerFactory emf = ... ;
EntityManager em = emf.createEntityManager();
try {
EntityTransaction t = em.getTransaction();
try {
t.begin();
// business logic to update the customer
em.merge(cust);
t.commit();
} finally {
if (t.isActive()) t.rollback();
}
} finally {
em.close();
}
}
Perhaps this nested structure could looks like a bit of a mess, but it is really needed in precence of transactions.

I hope Java 7 comes soon for help! As of Java 7, all this nesting will be avoided by using closures!