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?
It's not about CDI. EntityManager's life cycle depends on its type, which can be:
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.