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.
You can maintain the CDI bean state using CDI Scopes. Actually EntityManagerProducer#create()
missing the scope. What ever the RI of CDI you have configure/install in tomact either its Weld or OpenWebBean, you can define your cdi bean state as belwo.
@Produces @RequestScoped public EntityManager create() { return emf.createEntityManager(); }
Your problem are
1. CDI, JSF and JPA2. 2. Managing EntityManager lifecycle when using JPA in a non enterprise environment (e.g. Tomcat)
1. CDI, JSF and JPA2.
Tomcat container not support the CDI out the box, even not the JSF, you know developers had to supply the JSF jars them self's.JSF 2.2 has new CDI compatible scops @ViewScoped here's the CDI-only @FlowScoped which doesn't have an equivalent for @ManagedBean.
(1) Really If you are most interested to use CDI or CDI+JSF+JPA , then upgrade tomcat to TomEE or go with TomEE. Tomcat + Java EE = TomEE.The Java Enterprise Edition of Tomcat,With TomEE you get Tomcat with JPA.
(2) If you no control over upgrading the tomcat server, in that case you had to do i. Supply CDI and some others jar and configuration files along with weapp it self. ii. Installing CDI in tomcat (Weld, or OpenWebBeans these both are major CDI implementations)
(3) Tomcat 8. Tomcat 8 is aligned with Java EE 7.
2) Managing EntityManager lifecycle
Managing EntityManager lifecycle when using JPA in a non enterprise environment (e.g. Tomcat) or Java SE is a custom task. In this situation, you should consider the right scope of the EntityManager to use and while working with resources it's always important to ensure they are closed when not longer needed.
There are three main types of EntityManagers defined in JPA. Container Managed and Transaction Scoped Entity Managers Container Managed and Extended Scope Entity Managers Application Managed Entity Managers
Working with JPA there are two kind of resources we can take care of: EntityManager and transactions. In this case, you should consider the right scope of the EntityManager to use.
1. An EntityManager is not a heavyload object. There is no need to use the same EntityManger longer than needed, You can't use an EntityManager instance for the whole application lifecycle (application scope) for the EntityManager is not Thread-safe) 2. It's not safe to traverse lazy-loaded relationships once the EntityManager is closed (This situation will change as of JPA 2.0). i.)Method scope (i.e. instantiate/destroy one EntityManager in each business method). The method scope is not enough for every situation. There could be some scenarios where you'll need a wide scope, such as the following situations: i. When transactions spread multiple business methods. ii. Need to traverse lazy-loaded relationships outside a method (e.g. in a JSF page). In method scope be careful to ensure the EntityManger is always closed ii.)Request scope (on-demand creation of the EntityManager instance to use within the request service) EntityManager per HTTP request strategy with the following features: i. Creation on demand of the EntityManager. ii. Lazy closing of the EntityManager. The main benefit of this scope is derived from the delayed closing of the EntityManager (it will last as long as a HTTP request is in process). Every queried entity will be managed till the end of the request and therefore during the presentation phase (the render phase in JSF for instance).
In your case your are using application entity manger and application managed transaction it means its your code which is supposed to handle the transaction. In a nutshell it means:
You call:
entityManager.getTransaction().begin(); //to start a transaction
then if success you will ensure to call
entityManager.getTranasaction().commit(); //to commit changes to database
or in case of failure you will make sure to call:
entityManager.getTransaction().rollBack();
Now imagine you have a container, which knows when to call begin(), commit() or rollback()
, thats container managed transaction.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With