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

22,124

Solution 1

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.

Solution 2

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.

Solution 3

The main problem is that your entity manager producer has no scope. As a result, it's dependent which never gets cleaned up. You should provide a scope for your entity manager.

The other thing is that Apache DeltaSpike already has this solved. Why not use DeltaSpike? https://deltaspike.apache.org/documentation/jpa.html

Share:
22,124

Related videos on Youtube

Ioan
Author by

Ioan

Js lover.

Updated on May 05, 2020

Comments

  • Ioan
    Ioan about 4 years

    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?

    • Joshua Wilson
      Joshua Wilson over 10 years
      Are you willing to trying something other then Tomcat?
    • Ioan
      Ioan over 10 years
      @JoshuaWilson Generally yes, but I am kind of stuck with this environement. Which one would you recommend ? TomEE, Glassfish?
    • Joshua Wilson
      Joshua Wilson over 10 years
      If all you are doing is servlets and jsp then Tomcat is fine. When you start adding on all the pieces for JEE then you are building your own App server. You are at more risk and higher maintenance by doing this. I would recommend JBoss/Wildfly, it is free and it is designed to do what you want.
  • Ioan
    Ioan over 10 years
    Thanks for your answer. Why are you saying : "In your case your are using application entity manger " ? You meant entity manager factory?
  • Asif Bhutto
    Asif Bhutto over 10 years
    because of you are getting entity manger factory as like this Persistence.createEntityManagerFactory(TEST) and getting entity manger emf.getEntityManger(). Even if you will get entity manger like this @PersistenceUnit EntityManagerFactory emf; o invoke emf.createEntityManager(). It means you are using application-managed EntityManager. The application is responsible for creation and removal of EntityManager.
  • Ioan
    Ioan over 10 years
    Aha, ok, now it makes sense. Thanks.
  • Ioan
    Ioan over 10 years
    Thanks. Yes, is true what you are saying. One issue : if the entity manager is method-scoped then the lazy loading doesn't work anymore, correct ? So I need to disable all lazy loading from my entities ?
  • Ioan
    Ioan over 10 years
    Thanks. Your answer helped me. Still, I need to study a little bit the deltaspike framework, to see if fits for my needs.
  • Asif Bhutto
    Asif Bhutto over 10 years
    Yes i know entity manger not has scope in your code. emf has this scope means an object which is defined as @ApplicationScoped is created once for the duration of the application. scope not define the container manger entity manger or application mange entity manger. scope define the state/life cycle of CDI bean in CDI base/web application
  • V G
    V G over 10 years
    @Ioan you don't need to disable the lazy loading, you simply must be sure to load the lazy fields when needed (e.g by calling entity.getLazyCollection().size()), meaning if you know that those fields are needed, you will have to load them.
  • Asif Bhutto
    Asif Bhutto over 10 years
    @loan i have updated the answer. first i did not want to put all things because i though you know well the container vs entity manger and about local-resource tx and JTA transaction
  • Ioan
    Ioan over 10 years
    So as a final conclusion, I have to stick to method scoped entity manager, handling all transaction related (beginTransaction, commit, close), and if I need specific colections which are loaded lazily, I need to call size() on that collection, so that they are forced to be loaded before I commit the transaction and close the entity bean. Am I correct?
  • Yuri
    Yuri over 10 years
    @loan Yes, except for the lazy loading part ) Calling size() or any other method on a lazy association just to load it is often considered a bad practice. When there's a lazy association, to a collection or single entity, EM hides it behind a proxy object. That proxy is responsible for fetching data on demand. If it happens that you don't need that data there's no point in loading it, and if you do need it an EM will load it for you automatically when you start using that lazy-loaded collection or entity. So there's no need to artificially trigger fetching!
  • V G
    V G over 10 years
    @Yuri if the EntityManager will be closed (actually the associated transaction will not be active), working with a not loaded lazy field could throw an org.hibernate.LazyInitializationException (something with no session or session closed). Now I am not sure exactly what happens when the entityManager used for loading the entity was application-managed, but with a container-managed transactional this is almost sure to happen.
  • Yuri
    Yuri over 10 years
    @Andrei_I Don't confuse an open EM with an active transaction. Application-managed EMs can outlive a single transaction, nothing wrong with that. When it gets to fetching lazy-loaded associations, this has to happened in the same persistence context (that is, using the same, open, instance of EM) and within a transaction (may be a different one). You might get LazyInitializationException, which only means that the EM is closed, and NoActiveTransactionException, which is pretty self-explanatory )
  • V G
    V G over 10 years
    @Yuri what I wanted to point out (and you didn't mention): if the EM is closed (i.e outside CDI) the guy will get a LazyInitializationException when using a lazy-loaded property, because he didn't load the lazy relationship at the right moment. To avoid that, the guy must load the lazy relationship if needed.
  • V G
    V G over 10 years
    @Yuri good to know about calling size(). What are the alternatives? Example: A method must return a correctly loaded entity, but in that method that lazy field is not needed at all, but it will be used in the web layer (where the EM will be closed). What is the alternative to load that lazy field?
  • Yuri
    Yuri over 10 years
    @Andrei_I Use JPQL queries or Criteria API with FETCH JOIN: SELECT m FROM Magazine m LEFT JOIN FETCH m.articles This approach has an advantage over calling size(). Because you're very specific about what you want to fetch EM is more likely to generate optimized SQL, especially for complex queries. I suggest reading up on fetching strategies and "N+1 selects problem". This topic is nicely covered in Hibernate docs
  • V G
    V G over 10 years
    Oh, of course. But I was talking about a single entity, probably looked up because of convenience with a em.find(). Thanks anyway.
  • Erlan
    Erlan over 9 years
    @Yuri : clear and understandable, thanks. Q: I created *Home classes from reverse engineering and it automatically set container-managed entity manager structure(ie. there is one EM for every Home class, and /@transactional annotation). I removed /@transactional because I don't use EJB or Spring and faced problem. Solution was to create and close EM inside methods. Question is, could we create EM for classes and still use application-managed approach? Is there any way to use only one EM for each class?
  • Yuri
    Yuri over 9 years
    @Erlan short answer is yes. 'application-managed' means you can do whatever you want with EMs. there will be issues of db connection pool tuning and management of concurrent access to EMs and entities. but that's too big for discussion in comments maybe you should start a new question and give more context (code snippets, general structure of your app, your runtime environment, etc).
  • G. Demecki
    G. Demecki over 8 years
    Good answer. Also points to the DeltaSpike, which perfectly fits this scenario.
  • Halil
    Halil almost 7 years
    The question is about CDI. So, spring framework does not exist in the application.