5.3. Using JPA

Spring provides the following three methods of implementing Java Persistence API:
  • PersistenceUnit Deployed by the Container
  • PersistenceUnit Created by Spring
  • PersistenceContext and PersistenceUnit Injection

5.3.1. PersistenceUnit Deployed by the Container

Spring applications can retrieve the persistence units deployed by the container by looking them up in JNDI. In order to bind the entity manager or entity manager factory under a well-established name and subsequently look them up, applications can define them in the web.xml file.
Consider the following persistence unit defined in the persistence.xml file:

Example 5.6. Persistence Unit Definition


<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="2.0">
    <persistence-unit name="sportsclubPU">
        <jta-data-source>java:jboss/datasources/ExampleDS</jta-data-source>
    </persistence-unit>
</persistence>
In this case, you can bind the persistence context in JNDI as follows:

Example 5.7. Persistence context binding in JNDI


<web-app version="3.0"
    xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
    http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
    metadata-complete="true">
        
    <persistence-context-ref>
        <persistence-context-ref-name>example/em</persistence-context-ref-name>
        <persistence-unit-name>sportsclubPU</persistence-unit-name>
    </persistence-context-ref>
</web-app>
Spring applications can use either container-managed entity managers or application-managed entity managers. In this case, entity managers are provided with the corresponding entity manager factories. You can access a container-managed entity manager as a Spring Bean, as shown in the following example.

Example 5.8. Spring Bean Representing a Container-managed Entity manager

<jee:jndi-lookup id="entityManager" jndi-name="java:comp/env/example/em"/>
You can use this EntityManager directly when it is injected in component classes such as DAOs.

Example 5.9. Hibernate-based DAO: a SessionFactory is Injected Directly in the Bean

public class HibernateAccountDao {
  @Autowired EntityManager entityManager;

  public List<Account> getAllAccounts() {
     return entityManager.createQuery("SELECT a FROM Account").getResultList();
  }
  ...
}
Alternatively, you can use an EntityManagerFactory directly by either relying on Spring's ability to perform @PersistenceContext injection with a transactional EntityManager or when the scenario requires an application-managed EntityManager.
In this case, you can bind the persistence unit in JNDI as follows:

Example 5.10. Persistence unit binding in JNDI


<web-app version="3.0"
    xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
    http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" 
    metadata-complete="true">

    <persistence-unit-ref>
        <persistence-unit-ref-name>example/emf</persistence-unit-ref-name>
        <persistence-unit-name>sportsclubPU</persistence-unit-name>
    </persistence-unit-ref>
</web-app>
Following is an example of acquiring the entityManagerFactory from JNDI.

Example 5.11. Spring Bean Representing an Entity Manager Factory

<jee:jndi-lookup id="entityManagerFactory" jndi-name="java:comp/env/example/emf"/>
In general, for implementing transaction-aware components, you do not access EntityManagerFactories directly (for example a service that delegates to multiple DAOs that have to be enrolled in the same transaction). This is true even if Spring supports injection into fields annotated with @PersistenceContext when an EntityManagerFactory is provided. Rather, components must access the JNDI-bound EntityManager, as it is JTA-synchronized and is also shared with non-Spring components that use JPA (for example EJBs).
A comprehensive example of using JPA-driven data access in Spring-based applications is found in the Sportsclub example application, included in the JBoss Web Framework Kit distribution.

5.3.2. PersistenceUnit Created by Spring

When declaring transactions, Spring applications can specify that a transaction is intended to be read-only as shown in the following example.

Example 5.12. A Spring Method Declaring a Read-Only Transaction

@Transaction(readOnly = true)
public List<Account> getAllAccounts() {
    return entityManager.createQuery("SELECT a FROM Account").getResultList();
}
A read-only transaction means that the persistence context is discarded at the end of the transaction. Spring normally tries to block the persistence context from being discarded, but the @Transactional(readOnly=true) flag causes Spring to allow it. Application performance increases as the persistence context is discarded at the end of the transaction. Support for this mode is not obligatory in Spring, and applications are not expected to rely on this behaviour at all times.
This behaviour is supported by Spring-defined EntityManagerFactories for certain JPA providers (Hibernate is one of them), but not when the EntityManager or EntityManagerFactory is provided from JNDI. This means that a JTA-integrated LocalContainerEntityManagerFactoryBean must be used for any application that requires support for Read-Only persistence contexts. The following is a bean definition for Red Hat JBoss Enterprise Application Platform:

Example 5.13. A Spring-defined JTA-based Entity Manager Factory

<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="jpaVendorAdapter">
        <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"/>
    </property>
    <property name="jpaProperties">
        <props>
             <prop key="hibernate.transaction.jta.platform">
                 org.hibernate.service.jta.platform.internal.JBossAppServerJtaPlatform
             </prop>
         </props>
    </property>
</bean>
This bean definition requires the META-INF/persistence.xml file, which contains the persistence unit definition, to be present in the deployment. JBoss Enterprise Application Platform automatically deploys the persistence unit from the persistence.xml file. However, if you require to create a Spring-based EntityManager rather than use the container-managed EntityManagerFactory, prevent JBoss Enterprise Application Platform from deploying the persistence unit. Use one of the following methods to achieve this:
  • Configuring JBoss Enterprise Application Platform to not create EntityManagerFactory
  • Renaming or moving the persistence.xml file
Configuring JBoss Enterprise Application Platform to not create EntityManagerFactory
In the persistence.xml file, set the value of jboss.as.jpa.managed property to false. This stops JBoss Enterprise Application Platform from creating the EntityManagerFactory. By default, this property is set to true, which enables container managed JPA access to the persistence unit.

Example 5.14. Configuring JBoss Enterprise Application Platform to not create EntityManagerFactory


<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="bookingDatabase">
        <provider>org.hibernate.ejb.HibernatePersistence</provider>
        <class>org.springframework.webflow.samples.booking.User</class>
        <class>org.springframework.webflow.samples.booking.Booking</class>
        <class>org.springframework.webflow.samples.booking.Hotel</class>
        <properties>
            <property name="jboss.as.jpa.managed" value="false"/>
            <!-- other properties (omitted) -->
        </properties>
    </persistence-unit>
</persistence>
Renaming or moving the Persistence.xml file
Rename or move the persistence.xml file and use the persistenceXmlLocation property as follows:

Example 5.15. Renaming or moving the Persistence.xml file

<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
   <property name="persistenceXmlLocation" value="classpath*:META-INF/persistence-booking.xml"/>
	</bean>
Irrespective of where the persistence.xml file is located or what it is called, the WEB-INF/classes location is not scanned for persistent entries when the LocalContainerManagedEntityFactoryBean is used. In this case, the list of entities must be provided explicitly through one of the following methods:
  • By enumerating the persistent classes in the persistence.xml file (or other persistence unit configuration file name)
  • By packaging the entities in a JAR file and providing a <jar-file/> configuration entry in the persistence.xml file
  • Using a PersistenceUnitPostprocessor
The first two solutions are based on the standard functionality of JPA. The third solution is Spring-specific and involves implementing a PersistenceUnitPostprocessor class that adds the persistent classes directly to the PersistenceUnitInfo object, as in the following example:

Example 5.16. Example of a PersistenceUnitPostProcessor Implementation That Adds all Classes Annotated with @Entity

package org.springframework.webflow.samples.booking;

import java.io.IOException;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.orm.jpa.persistenceunit.MutablePersistenceUnitInfo;
import org.springframework.orm.jpa.persistenceunit.PersistenceUnitPostProcessor;

public class MyPersistenceUnitPostProcessor implements PersistenceUnitPostProcessor
{

   @Autowired
   private ResourcePatternResolver resourceLoader;

   public void postProcessPersistenceUnitInfo(MutablePersistenceUnitInfo mutablePersistenceUnitInfo)
   {
      try
      {
         Resource[] resources = resourceLoader.getResources("classpath:org/myexample/*.class");
         for (Resource resource : resources)
         {
            CachingMetadataReaderFactory cachingMetadataReaderFactory = new CachingMetadataReaderFactory();
            MetadataReader metadataReader = cachingMetadataReaderFactory.getMetadataReader(resource);
            if (metadataReader.getAnnotationMetadata().isAnnotated(javax.persistence.Entity.class.getName()))
            {
               mutablePersistenceUnitInfo.addManagedClassName(metadataReader.getClassMetadata().getClassName());
            }
         }
         mutablePersistenceUnitInfo.setExcludeUnlistedClasses(true);
      }
      catch (IOException e)
      {
         throw new RuntimeException(e);
      }
   }
}
You can inject a bean of PersistenceUnitPostprocessor class in the LocalContainerEntityManagerFactoryBean as follows:

Example 5.17. Adding the PersistenceUnitPostProcessor to the Context Definition

<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<!-- other properties -->
  <property name="persistenceUnitPostProcessors">
    <list>
      <bean class="org.springframework.webflow.samples.booking.MyPersistenceUnitPostProcessor"/>
    </list>
  </property>
</bean>

5.3.3. PersistenceContext and PersistenceUnit Injection

Spring injects @PersistenceContext into Spring components by default. In order to do so, applications must have access to an EntityManagerFactory bean (either created by Spring or looked up in JNDI).
You can rename the persistence.xml file to use a Spring-based EntityManagerFactory. However, the presence of @PersistenceContext or @PersistenceUnit annotations in Spring can cause deployment errors when the persistence.xml file is renamed. The errors occur because in a Java EE 6 environment, the @PersistenceContext and @PersistenceUnit annotations are used for supporting the container-driven injection of container-managed persistence contexts and persistence units, respectively. During deployment, Red Hat JBoss Enterprise Application Platform scans the deployment classes and validates for the presence of such annotations, the corresponding managed persistence units exist as well. This is not the case if the persistence.xml file is renamed.
To resolve this, either disable the scanning of deployment classes, or use the @Autowire injection.
In case of Spring-based deployments, the injection of components and resources is done by Spring and not by JBoss Enterprise Application Platform, so in most cases it is not necessary for JBoss Enterprise Application Platform to perform the scanning. For web applications conforming to the Servlet 2.5 specification and higher, this can be set up through the metadata-complete attribute (applications that use the Servlet 2.4 standard do not exhibit this problem, as annotation-based injection is not supported by the container).

Example 5.18. Metadata-complete Flag to Disable Class Scanning by Red Hat JBoss Enterprise Application Platform


<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
    http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
    metadata-complete="true">
An alternative is to use @Autowired instead of @PersistenceUnit/@PersistenceContext. In the case of @PersistenceUnit/EntityManagerFactory injection you are required to only replace the annotation, but @PersistenceContext requires an extra step.
If you are using a container-deployed EntityManagerFactory, bind the EntityManager in JNDI and use a JNDI lookup bean to retrieve it.
If you are using a Spring-deployed EntityManagerFactory, autowire a transaction-aware EntityManager by adding a SharedEntityManagerBean definition as in the following example.

Example 5.19. SharedEntityManager Bean to Create an Injectable Transaction-aware EntityManager

<bean id="entityManagerWrapper" class="org.springframework.orm.jpa.support.SharedEntityManagerBean">
   <property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>