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() {However, as you can guess, this code doesn't ensure the EntityManager is always closed when the method finishes.
EntityManagerFactory emf = ... ;
EntityManager em = emf.createEntityManager();
// business logic
em.close();
}
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() {Do you think this is enough to dispose all the EntityManger's resources?
EntityManagerFactory emf = ... ;
EntityManager em = emf.createEntityManager();
try {
// business logic
} finally {
em.close();
}
}
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) {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?
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();
}
}
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) {Perhaps this nested structure could looks like a bit of a mess, but it is really needed in precence of transactions.
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();
}
}
I hope Java 7 comes soon for help! As of Java 7, all this nesting will be avoided by using closures!