Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

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?

like image 433
Ioan Avatar asked Nov 23 '13 18:11

Ioan


2 Answers

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.

like image 128
Yuri Avatar answered Sep 19 '22 19:09

Yuri


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.

like image 27
Asif Bhutto Avatar answered Sep 20 '22 19:09

Asif Bhutto