How to manage EntityManager life-cycle in CDI environment (using Tomcat)

Ioan picture Ioan · Nov 23, 2013 · Viewed 21.4k times · Source

I am developing one application and I have started to use CDI along with JSF and JPA. The web container is Tomcat.

I am very confused about EntityManager life-cycle in my CDI beans and I would need a good advise to clear some stuff in my mind. Generally what I've read is that EntityManager should be used mainly in a Java EE container, injecting it using PersistenceContext annotation. So then the container takes care about its life. However, if you do not use Java EE container(as Tomcat), then I need to manage my EntityManager's life.

Which are my best options now, using Tomcat, CDI, JSF and JPA? What I am currently doing now is the following:

public class EntityManagerFactoryProducer {

    public static final String TEST = "test";

    @Produces
    @ApplicationScoped
    public EntityManagerFactory create() {
        return Persistence.createEntityManagerFactory(TEST);
    }

    public void destroy(@Disposes
    EntityManagerFactory factory) {
        factory.close();
    }
}

public class EntityManagerProducer {

    @Inject
    private transient Logger logger;

    @Inject
    private EntityManagerFactory emf;

    @Produces
    public EntityManager create() {
        return emf.createEntityManager();
    }

    public void destroy(@Disposes
    EntityManager em) {
        em.close();
        logger.debug(String.format("%s Entity manager was closed", em));
    }
}

@Named
@ViewScoped
@Interceptors(LoggingInterceptor.class)
public class ProductBacking implements Serializable {

    @Inject
    private ProductDAO productDAO;

@ViewScoped
public class ProductDAOImpl implements ProductDAO, Serializable {
    private static final long serialVersionUID = 4806788420578024259L;

    private static final int MAX_RANDOMIZED_ELEMENTS = 3000;

    @Inject
    private transient Logger logger;

    @Inject
    private EntityManager entityManager;

    @Override
    public List<Product> getSuggestedProducts() {
        logger.debug(String.format("%s Entity manager get products", entityManager));

        return entityManager.createQuery("SELECT p FROM Product p ORDER BY random()", Product.class).setMaxResults(
                MAX_RANDOMIZED_ELEMENTS).getResultList();
    }

    @Override
    public void saveProduct(Product product) {
        logger.debug(String.format("%s Entity manager save product", entityManager));

        entityManager.getTransaction().begin();
        entityManager.merge(product);
        entityManager.getTransaction().commit();
    }

    @PreDestroy
    void destroy() {
        entityManager.close();
    }
}

So basically I am just using plain CDI to accomplish this. However, I am not sure if this is standard way of doing it, and what is more important, I do not know how to close the EntityManager after bean life is over. As as summary: I end up with many unclosed connections (EntityManagers), so memory leak.

Can someone help me understand how should I proceed?

Answer

Yuri picture Yuri · Nov 26, 2013

It's not about CDI. EntityManager's life cycle depends on its type, which can be:

  1. container-managed transactional,
  2. container-managed extended,
  3. application-managed.

The first two are only available in a full-blown application server. So if you're going to stick with a servlet container you're narrowed to the 3rd option.

You will have to explicitly open and close EMs in your application. It's straightforward: create an application-wide instance of EntityManagerFactory, inject it to all your beans. When you need an EM just create it, use and then immediately close without waiting for your bean's context to end. Because in this configuration an open EntityManager will retain a connection and with long-lived beans you'll run out of connections. You can find an easy and comprehensive explanation in the Obtaining a JPA Database Connection section of the ObjectDB manual.