How to manage EntityManager life-cycle in CDI environment (using Tomcat)
cdi, hibernate, jakarta-ee, java, jpa
Solution
It's not about CDI. EntityManager's life cycle depends on its type, which can be:
- container-managed transactional,
- container-managed extended,
- 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.
Problem
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 (`EntityManager`s), so memory leak. Can someone help me understand how should I proceed?