Injecting EntityManager with a producer in tomcat

Yashar picture Yashar · May 30, 2013 · Viewed 6.9k times · Source

I am running a project using Hibernate and Weld CDI on tomcat 7. I have write a ServletContextListener to create the EntityManagerFactory and EntityManager during application startup.

public class PersistenceListener implements ServletContextListener {

     private static EntityManagerFactory entityManagerFactory;

     public void contextInitialized(ServletContextEvent sce){
     ServletContext context = sce.getServletContext();
     entityManagerFactory = Persistence.createEntityManagerFactory("hibernate-test");
     }

     public void contextDestroyed(ServletContextEvent sce) {
     entityManagerFactory.close();
     }


     public static EntityManager createEntityManager() {
            if (entityManagerFactory == null) {
                throw new IllegalStateException("Context is not initialized yet.");
            }

            return entityManagerFactory.createEntityManager();
        }

}

I can use my entityManager in my test class (it is an arquillian test class) simply by creating it through the following code

EntityManager em = PersistenceListener.createEntityManager();
               em.getTransaction().begin();
                   em.createQuery("delete from Game").executeUpdate();
                   em.getTransaction().commit();

here is the complete code for my test class

    @RunWith(Arquillian.class)
    public class HibernateTestSample {

           @Deployment
           public static WebArchive createTestArchive()
           {
               MavenDependencyResolver resolver = DependencyResolvers.use(
                        MavenDependencyResolver.class).loadMetadataFromPom("pom.xml");

               WebArchive webArchive=  ShrinkWrap
                    .create(WebArchive.class, "ROOT.war")
                    .addClasses(CdiTestBean.class,HibernateListener.class,PersistenceListener.class)
                    .addAsLibraries(
                                resolver.artifact("org.jboss.weld.servlet:weld-servlet")
//                              .artifact("org.hibernate.javax.persistence:hibernate-jpa-2.0-api")
                                .artifact("org.apache.tomcat:tomcat-dbcp")
                                .artifact("org.hibernate:hibernate-entitymanager")
                                .artifact("org.hibernate:hibernate-validator")
                                .artifact("org.hibernate:hibernate-core")   
                                .artifact("com.h2database:h2")
                                .artifact("mysql:mysql-connector-java")
                                .resolveAs(GenericArchive.class))

                    .addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml")
                    .addAsWebInfResource("test-persistence.xml", "classes/META-INF/persistence.xml")
                    .addAsWebInfResource("hibernate.cfg.xml", "classes/hibernate.cfg.xml") 
//                  .addAsWebInfResource("context.xml", "classes/META-INF/context.xml")
                    .addAsManifestResource("context.xml", "context.xml")
                    .setWebXML("hibernate-web.xml");
              System.out.println(webArchive.toString(true));

              return webArchive;
           }


           @Test
           public void myTest()
                   throws Exception {                                                      

               EntityManager em = PersistenceListener.createEntityManager();
               em.getTransaction().begin();
                       em.createQuery("delete from Game").executeUpdate();
                   em.getTransaction().commit();
                  ...............
                      .......
                      ...


           }
    }

but I want to inject my entityManager to my class. I read in an other post that I cannot use @PersistenceContext in my class, therefor I decided to use a producer to inject my entity manager. but it doesn't work for my, please tell me what am i doing wrong here (I am quite new in CDI )

here is my new ServletContextListener

public class PersistenceListener implements ServletContextListener {

     private static EntityManagerFactory entityManagerFactory;

     @Produces
     private EntityManager entityManager;

     public void contextInitialized(ServletContextEvent sce){
     ServletContext context = sce.getServletContext();
     entityManagerFactory = Persistence.createEntityManagerFactory("hibernate-test");
     createEntityManager();
     }

     public void contextDestroyed(ServletContextEvent sce) {
     entityManagerFactory.close();
     }


     public void createEntityManager() {
            if (entityManagerFactory == null) {
                throw new IllegalStateException("Context is not initialized yet.");
            }

            this.entityManager =  entityManagerFactory.createEntityManager();
        }

and I am injecting in my testclass

@Inject
private EntityManager em;

It is null

Answer

Karl Kildén picture Karl Kildén · May 31, 2013

The instance created through the Servlet specification by implementing ServletContextListener is not the same instance as CDI will create later on to resolve your @Produces field.

Further I recommend this informative blog post about the EntityManager: http://struberg.wordpress.com/2012/04/25/is-there-a-way-to-fix-the-jpa-entitymanager/

It might be slighly outdated though because if I recall correctly I have seen @TransactionScoped in the works over at deltaspike.

Lastly as you may have figured by now having a singleton EntityManager is not what you want but for the sake of completeness the miss in synergy between your ContextListener and CDI can be somewhat fixed by using CdiCtrl from Deltaspike and BeanProvider from Deltspike Core.

How to attach CDI ApplicationScope to to your current thread http://struberg.wordpress.com/2012/03/17/controlling-cdi-containers-in-se-and-ee/

And then you can pickup a bean that is annotated with @ApplicationScoped and set it up. The pickup is done with MyBean myBean = BeanProvider.getContextualReference(MyBean.class, false); http://deltaspike.apache.org/deltaspike/core.html

Hopefully CDI 1.1 will make this a little more fluent

Update: The talk on the Deltaspike list is to have 0.5 be a smaller release in about a month. Hopefully the new Servlet module will solve this nicely.

https://issues.apache.org/jira/browse/DELTASPIKE-376 https://issues.apache.org/jira/browse/DELTASPIKE-377