Thursday, February 9, 2017

CDI, Tomcat and JPA

Contexts and Dependency Injection (CDI) for the Java EE platform is one of several Java EE 6 features that help to knit together the web tier and the transactional tier of the Java EE platform. CDI is a set of services that, used together, make it easy for developers to use enterprise beans along with JavaServer Faces technology in web applications. Designed for use with stateful objects, CDI also has many broader uses, allowing developers a great deal of flexibility to integrate various kinds of components in a loosely coupled but typesafe way.

Tomcat (Tomcat 7 in this blog) is a web container. It is not a Java EE container. So it does not recognize @PersistenceContex, @EJB, etc.

Say we have the persistence.xml file for JPA(Java Persistence API) in the META-INF folder.

<?xml version="1.0" encoding="UTF-8"?> 
<persistence xmlns="http://java.sun.com/xml/ns/persistence" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd" 
version="2.0"> 
   <persistence-unit name="my_unit"> 
     <provider>org.hibernate.ejb.HibernatePersistence</provider> 
     <properties> 
        <property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5InnoDBDialect" /> 
        <property name="hibernate.hbm2ddl.auto" value="update" /> 
        <property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver" /> 
        <property name="hibernate.connection.username" value="username" /> 
        <property name="hibernate.connection.password" value="pwd" /> 
        <property name="hibernate.connection.url" value="jdbc:mysql://localhost/somepath" /> 
     </properties> 
   </persistence-unit> 
</persistence> 

 

In a Java EE server, you can inject EntityManager as below:

public class MyApp {

    @PersistenceContext(unitName = "my-unit")
    private EntityManager entityManager;

    public void doSomething {
        entityManager.persist(...);
    }
}
 

But this won't work in Tomcat. In Tomcat, typically you need to do the following to get the EntityManager.

 EntityManagerFactory emf = Persistence.createEntityManagerFactory("my_unit"); 
 EntityManager em = emf.createEntityManager(); 
 em.getTransaction().begin(); 
   // do some thing here
 em.getTransaction().commit(); 
 

There is a way so that you can still use @PersistenceContext in Tomcat. The trick is to use the Spring class PersistenceAnnotationBeanPostProcessor. Below is the Spring documentation of this class.

This post-processor will inject sub-interfaces of EntityManagerFactory and EntityManager if the annotated fields or methods are declared as such. The actual type will be verified early, with the exception of a shared ("transactional") EntityManager reference, where type mismatches might be detected as late as on the first actual invocation.

Note: In the present implementation, PersistenceAnnotationBeanPostProcessor only supports @PersistenceUnit and @PersistenceContext with the "unitName" attribute, or no attribute at all (for the default unit). If those annotations are present with the "name" attribute at the class level, they will simply be ignored, since those only serve as deployment hint (as per the Java EE 5 specification).

This post-processor can either obtain EntityManagerFactory beans defined in the Spring application context (the default), or obtain EntityManagerFactory references from JNDI ("persistence unit references"). In the bean case, the persistence unit name will be matched against the actual deployed unit, with the bean name used as fallback unit name if no deployed name found. Typically, Spring's LocalContainerEntityManagerFactoryBean will be used for setting up such EntityManagerFactory beans. Alternatively, such beans may also be obtained from JNDI, e.g. using the jee:jndi-lookup XML configuration element (with the bean name matching the requested unit name). In both cases, the post-processor definition will look as simple as this:

   <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"/>

So in your Spring context.xml file, you can put something like the following:

 <bean id="entityManagerFactory"
  class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
  <property name="persistenceXmlLocation" value="classpath*:META-INF/persistence.xml" />
  <property name="persistenceUnitName" value="my_unit" />
  <property name="dataSource" ref="dataSource" />
  <property name="jpaVendorAdapter">
   <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
    <property name="showSql" value="true" />
   </bean>
  </property>
  <property name="loadTimeWeaver">
   <bean
    class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver" />
  </property>
   <property name="jpaDialect">
            <bean class="......"/>
        </property>
 </bean>
 <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
  

No comments:

Post a Comment