13.5 JPA

The Spring JPA, available under the org.springframework.orm.jpa package, offers comprehensive support for the Java Persistence API in a similar manner to the integration with Hibernate or JDO, while being aware of the underlying implementation in order to provide additional features.

13.5.1 Three options for JPA setup in a Spring environment

The Spring JPA support offers three ways of setting up the JPA EntityManagerFactory that will be used by the application to obtain an entity manager.

13.5.1.1 LocalEntityManagerFactoryBean

[Note]Note

Only use this option in simple deployment environments such as stand-alone applications and integration tests.

The LocalEntityManagerFactoryBean creates an EntityManagerFactory suitable for simple deployment environments where the application uses only JPA for data access. The factory bean uses the JPA PersistenceProvider autodetection mechanism (according to JPA's Java SE bootstrapping) and, in most cases, requires you to specify only the persistence unit name:

<beans>

   <bean id="myEmf" class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean">
      <property name="persistenceUnitName" value="myPersistenceUnit"/>
   </bean>

</beans>

This form of JPA deployment is the simplest and the most limited. You cannot refer to an existing JDBC DataSource bean definition and no support for global transactions exists. Furthermore, weaving (byte-code transformation) of persistent classes is provider-specific, often requiring a specific JVM agent to specified on startup. This option is sufficient only for stand-alone applications and test environments, for which the JPA specification is designed.

13.5.1.2 Obtaining an EntityManagerFactory from JNDI

[Note]Note

Use this option when deploying to a Java EE 5 server. Check your server's documentation on how to deploy a custom JPA provider into your server, allowing for a different provider than the server's default.

Obtaining an EntityManagerFactory from JNDI (for example in a Java EE 5 environment), is simply a matter of changing the XML configuration:

<beans>

    <jee:jndi-lookup id="myEmf" jndi-name="persistence/myPersistenceUnit"/>

</beans>

This action assumes standard Java EE 5 bootstrapping: the Java EE server autodetects persistence units (in effect, META-INF/persistence.xml files in application jars) and persistence-unit-ref entries in the Java EE deployment descriptor (for example, web.xml) and defines environment naming context locations for those persistence units.

In such a scenario, the entire persistence unit deployment, including the weaving (byte-code transformation) of persistent classes, is up to the Java EE server. The JDBC DataSource is defined through a JNDI location in the META-INF/persistence.xml file; EntityManager transactions are integrated with the server's JTA subsystem. Spring merely uses the obtained EntityManagerFactory, passing it on to application objects through dependency injection, and managing transactions for the persistence unit, typically through JtaTransactionManager.

If multiple persistence units are used in the same application, the bean names of such JNDI-retrieved persistence units should match the persistence unit names that the application uses to refer to them, for example, in @PersistenceUnit and @PersistenceContext annotations.

13.5.1.3 LocalContainerEntityManagerFactoryBean

[Note]Note

Use this option for full JPA capabilities in a Spring-based application environment. This includes web containers such as Tomcat as well as stand-alone applications and integration tests with sophisticated persistence requirements.

The LocalContainerEntityManagerFactoryBean gives full control over EntityManagerFactory configuration and is appropriate for environments where fine-grained customization is required. The LocalContainerEntityManagerFactoryBean creates a PersistenceUnitInfo instancebased on the persistence.xml file, the supplied dataSourceLookup strategy, and the specified loadTimeWeaver. It is thus possible to work with custom data sources outside of JNDI and to control the weaving process. The following example shows a typical bean definition for a LocalContainerEntityManagerFactoryBean:

<beans>
        
 <bean id="myEmf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
  <property name="dataSource" ref="someDataSource"/>
  <property name="loadTimeWeaver">
    <bean class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver"/>
  </property>
 </bean>
 
</beans>

The following example shows a typical persistence.xml file:

<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="1.0">

  <persistence-unit name="myUnit" transaction-type="RESOURCE_LOCAL">
    <mapping-file>META-INF/orm.xml</mapping-file>
    <exclude-unlisted-classes/>
  </persistence-unit>

</persistence>
[Note]Note

The exclude-unlisted-classes element always indicates that no scanning for annotated entity classes is supposed to occur, in order to support the <exclude-unlisted-classes/> shortcut. This is in line with the JPA specification, which suggests that shortcut, but unfortunately is in conflict with the JPA XSD, which implies false for that shortcut. Consequently, <exclude-unlisted-classes> false </exclude-unlisted-classes/> is not supported. Simply omit the exclude-unlisted-classes element if you want entity class scanning to occur.

Using the LocalContainerEntityManagerFactoryBean is the most powerful JPA setup option, allowing for flexible local configuration within the application. It supports links to an existing JDBC DataSource, supports both local and global transactions, and so on. However, it also imposes requirements on the runtime environment, such as the availability of a weaving-capable class loader if the persistence provider demands byte-code transformation.

This option may conflict with the built-in JPA capabilities of a Java EE 5 server. In a full Java EE 5 environment, consider obtaining your EntityManagerFactory from JNDI. Alternatively, specify a custom persistenceXmlLocation on your LocalContainerEntityManagerFactoryBean definition, for example, META-INF/my-persistence.xml, and only include a descriptor with that name in your application jar files. Because the Java EE 5 server only looks for default META-INF/persistence.xml files, it ignores such custom persistence units and hence avoid conflicts with a Spring-driven JPA setup upfront. (This applies to Resin 3.1, for example.)

The LoadTimeWeaver interface is a Spring-provided class that allows JPA ClassTransformer instances to be plugged in a specific manner, depending whether the environment is a web container or application server. Hooking ClassTransformers through a Java 5 agent typically is not efficient. The agents work against the entire virtual machine and inspect every class that is loaded, which is usually undesirable in a production server environment.

Spring provides a number of LoadTimeWeaver implementations for various environments, allowing ClassTransformer instances to be applied only per class loader and not per VM.

The following sections will discuss typical JPA weaving setup on Tomcat and with Spring's VM agent. See Section 7.8.4.5, “Spring configuration” in the AOP chapter for details on how to set up general load-time weaving with Tomcat, the VM agent, WebLogic, OC4J, GlassFish, and Resin.

Tomcat load-time weaving setup (5.0+)

Apache Tomcat's default class loader does not support class transformation but does allow the use of custom class loaders. Spring offers the TomcatInstrumentableClassLoader (in the org.springframework.instrument.classloading.tomcat package), which extends the Tomcat class loader (WebappClassLoader), and allows JPA ClassTransformer instances to enhance all classes loaded by it. In short, JPA transformers are applied only inside a specific web application that uses the TomcatInstrumentableClassLoader.

To use the custom class loader on:

  1. Copy spring-tomcat-weaver.jar into $CATALINA_HOME/server/lib,where $CATALINA_HOME represents the root of the Tomcat installation.

  2. Instruct Tomcat to use the custom class loader instead of the default one by editing the web application context file:

    <Context path="/myWebApp" docBase="/my/webApp/location">
        <Loader loaderClass="org.springframework.instrument.classloading.tomcat.TomcatInstrumentableClassLoader"/>
    </Context>

    Tomcat 5.0.x and 5.5.x series support several context locations: server configuration file ($CATALINA_HOME/conf/server.xml), the default context configuration ($CATALINA_HOME/conf/context.xml) that affects all deployed web applications and per-web application configurations, deployed on the server ($CATALINA_HOME/conf/[enginename]/[hostname]/my-webapp-context.xml) side or inside the web application (your-webapp.war/META-INF/context.xml). For efficiency, inside the web-app configuration style is recommended because only applications that use JPA will use the custom c lass loader. See the Tomcat 5.x documentation for more details about available context locations.

    Tomcat versions prior to 5.5.20 contained a bug in the XML configuration parsing that prevented usage of the Loader tag inside server.xml, regardless of whether a class loader is specified or whether it is the official or a custom one. See Tomcat's bugzilla for more details.

    In Tomcat 5.5.20 or later, you can set useSystemClassLoaderAsParent to false to fix the problem:

    <Context path="/myWebApp" docBase="/my/webApp/location">
        <Loader loaderClass="org.springframework.instrument.classloading.tomcat.TomcatInstrumentableClassLoader"
                useSystemClassLoaderAsParent="false"/>
    </Context>
  1. Copy spring-tomcat-weaver.jar into $CATALINA_HOME/lib, where $CATALINA_HOME represents the root of the Tomcat installation)

  2. Instruct Tomcat to use the custom class loader (instead of the default) by editing the web application context file:

    <Context path="/myWebApp" docBase="/my/webApp/location">
        <Loader loaderClass="org.springframework.instrument.classloading.tomcat.TomcatInstrumentableClassLoader"/>
    </Context>

    The Tomcat 6.0.x (similar to 5.0.x/5.5.x) series supports several context locations: server configuration file ($CATALINA_HOME/conf/server.xml), the default context configuration ($CATALINA_HOME/conf/context.xml) that affects all deployed web applications and per-web application configurations, deployed on the server ($CATALINA_HOME/conf/[enginename]/[hostname]/my-webapp-context.xml) side or inside the web application (your-webapp.war/META-INF/context.xml). For efficiency, inside the web-app configuration style is recommended because only applications that use JPA will use the custom class loader. See the Tomcat 6.0.x documentation for more details about available context locations.

  • Tomcat 5.0.x/5.5.x

  • Tomcat 6.0.x

The last step required on all Tomcat versions is to use the appropriate LoadTimeWeaver when you configure LocalContainerEntityManagerFactoryBean:

<bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
  <property name="loadTimeWeaver">
    <bean class="org.springframework.instrument.classloading.ReflectiveLoadTimeWeaver"/>
  </property>
</bean>

Using this technique, JPA applications relying on instrumentation, and can run in Tomcat without needing an agent. This is important especially when Tomcat is hosting applications that rely on different JPA implementations because the JPA transformers are applied only at class loader level and thus are isolated from each other.

[Note]Note

If you use TopLink Essentials as a JPA provider under Tomcat, place the toplink-essentials JAR under $CATALINA_HOME/shared/lib folder instead of inside your war.

General load-time weaving with the VM agent

For environments that require class instrumentation but are not supported by the existing LoadTimeWeaver implementations, a JDK agent can be the only solution. For such cases, Spring provides InstrumentationLoadTimeWeaver, which requires a Spring-specific (but very general) VM agent, spring-agent.jar:

<bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
  <property name="loadTimeWeaver">
    <bean class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver"/>
  </property>
</bean>

You must start the virtual machine with the Spring agent, by supplying the following JVM options:

-javaagent:/path/to/spring-agent.jar
Context-wide load-time weaver setup

In Spring 2.5 and later, you can configure a context-wide LoadTimeWeaver using the context:load-time-weaver configuration element. Such a global weaver is picked up by all JPA LocalContainerEntityManagerFactoryBeans automatically.

This is the preferred way of setting up a load-time weaver, delivering autodetection of the platform (WebLogic, OC4J, GlassFish, Tomcat, Resin, or VM agent) and automatic propagation of the weaver to all weaver-aware beans.

<context:load-time-weaver/>

<bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    ...
</bean>

See Section 7.8.4.5, “Spring configuration” for details on how to set up general load-time weaving, covering Tomcat and the VM agent as well as WebLogic, OC4J, GlassFish and Resin.

13.5.1.4 Dealing with multiple persistence units

For applications that rely on multiple persistence units locations, stored in various JARS in the classpath, for example, Spring offers the PersistenceUnitManager to act as a central repository and to avoid the persistence units discovery process, which can be expensive. The default implementation allows multiple locations to be specified that are parsed and later retrieved through the persistence unit name. (By default, the classpath is searched for META-INF/persistence.xml files.)

<bean id="pum" class="org.springframework.orm.jpa.persistenceunit.DefaultPersistenceUnitManager">
  <property name="persistenceXmlLocation">
    <list>
     <value>org/springframework/orm/jpa/domain/persistence-multi.xml</value>
     <value>classpath:/my/package/**/custom-persistence.xml</value>
     <value>classpath*:META-INF/persistence.xml</value>
    </list>
  </property>
  <property name="dataSources">
   <map>
    <entry key="localDataSource" value-ref="local-db"/>
    <entry key="remoteDataSource" value-ref="remote-db"/>
   </map>
  </property>
  <!-- if no datasource is specified, use this one -->
  <property name="defaultDataSource" ref="remoteDataSource"/>
</bean>

<bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
  <property name="persistenceUnitManager" ref="pum"/>
</bean>

The default implementation allows customization of the PersistenceUnitInfo instances, before they are fed to the JPA provider, declaratively through its properties, which affect all hosted units, or programmatically, through the PersistenceUnitPostProcessor, which allows persistence unit selection. If no PersistenceUnitManager is specified, one is created and used internally by LocalContainerEntityManagerFactoryBean.

13.5.2 Implementing DAOs based on plain JPA

[Note]Note

Although EntityManagerFactory instances are thread-safe, EntityManager instances are not. The injected JPA EntityManager behaves like an EntityManager fetched from an application server's JNDI environment, as defined by the JPA specification. It delegates all calls to the current transactional EntityManager, if any; otherwise, it falls back to a newly created EntityManager per operation, in effect making its usage thread-safe.

It is possible to write code against the plain JPA without any Spring dependencies, by using an injected EntityManagerFactory or EntityManager. Spring can understand @PersistenceUnit and @PersistenceContext annotations both at field and method level if a PersistenceAnnotationBeanPostProcessor is enabled. A plain JPA DAO implementation using the @PersistenceUnit annotation might look like this:

public class ProductDaoImpl implements ProductDao {

    private EntityManagerFactory emf;

    @PersistenceUnit
    public void setEntityManagerFactory(EntityManagerFactory emf) {
        this.emf = emf;
    }

    public Collection loadProductsByCategory(String category) {
        EntityManager em = this.emf.createEntityManager();
        try {
             Query query = em.createQuery("from Product as p where p.category = ?1");
             query.setParameter(1, category);
             return query.getResultList();
        }
        finally {
            if (em != null) {
                em.close();
            }
        }
    }
}

The DAO above has no dependency on Spring and still fits nicely into a Spring application context. Moreover, the DAO takes advantage of annotations to require the injection of the default EntityManagerFactory:

<beans>

  <!-- bean post-processor for JPA annotations -->
  <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"/>

  <bean id="myProductDao" class="product.ProductDaoImpl"/>

</beans>

As an alternative to defining a PersistenceAnnotationBeanPostProcessor explicitly, consider using the Spring context:annotation-config XML element in your application context configuration. Doing so automatically registers all Spring standard post-processors for annotation-based configuration, including CommonAnnotationBeanPostProcessor and so on.

<beans>

  <!-- post-processors for all standard config annotations -->
  <context:annotation-config/>

  <bean id="myProductDao" class="product.ProductDaoImpl"/>

</beans>

The main problem with such a DAO is that it always creates a new EntityManager through the factory. You can avoid this by requesting a transactional EntityManager (also called "shared EntityManager" because it is a shared, thread-safe proxy for the actual transactional EntityManager) to be injected instead of the factory:

public class ProductDaoImpl implements ProductDao {

    @PersistenceContext
    private EntityManager em;

    public Collection loadProductsByCategory(String category) {
       Query query = em.createQuery("from Product as p where p.category = :category");
       query.setParameter("category", category);
       return query.getResultList(); 
    }
}

The @PersistenceContext annotation has an optional attribute type, which defaults to PersistenceContextType.TRANSACTION. This default is what you need to receive a shared EntityManager proxy. The alternative, PersistenceContextType.EXTENDED, is a completely different affair: This results in a so-called extended EntityManager, which is not thread-safe and hence must not be used in a concurrently accessed component such as a Spring-managed singleton bean. Extended EntityManagers are only supposed to be used in stateful components that, for example, reside in a session, with the lifecycle of the EntityManager not tied to a current transaction but rather being completely up to the application.

The injected EntityManager is Spring-managed (aware of the ongoing transaction). It is important to note that even though the new DAO implementation uses method level injection of an EntityManager instead of an EntityManagerFactory, no change is required in the application context XML due to annotation usage.

The main advantage of this DAO style is that it only depends on Java Persistence API; no import of any Spring class is required. Moreover, as the JPA annotations are understood, the injections are applied automatically by the Spring container. This is appealing from a non-invasiveness perspective, and might feel more natural to JPA developers.

13.5.3 Transaction Management

[Note]Note

You are strongly encouraged to read Section 10.5, “Declarative transaction management” if you have not done so, to get a more detailed coverage of Spring's declarative transaction support.

To execute service operations within transactions, you can use Spring's common declarative transaction facilities. For example:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans 
       http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
       http://www.springframework.org/schema/tx 
       http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
       http://www.springframework.org/schema/aop 
       http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">

  <bean id="myTxManager" class="org.springframework.orm.jpa.JpaTransactionManager">
    <property name="entityManagerFactory" ref="myEmf"/>
  </bean>

  <bean id="myProductService" class="product.ProductServiceImpl">
    <property name="productDao" ref="myProductDao"/>
  </bean>
  
  <aop:config>
    <aop:pointcut id="productServiceMethods" expression="execution(* product.ProductService.*(..))"/>
    <aop:advisor advice-ref="txAdvice" pointcut-ref="productServiceMethods"/>
  </aop:config>

  <tx:advice id="txAdvice" transaction-manager="myTxManager">
    <tx:attributes>
      <tx:method name="increasePrice*" propagation="REQUIRED"/>
      <tx:method name="someOtherBusinessMethod" propagation="REQUIRES_NEW"/>
      <tx:method name="*" propagation="SUPPORTS" read-only="true"/>
    </tx:attributes>
  </tx:advice>

</beans>

Spring JPA allows a configured JpaTransactionManager to expose a JPA transaction to JDBC access code that accesses the same JDBC DataSource, provided that the registered JpaDialect supports retrieval of the underlying JDBC Connection. Out of the box, Spring provides dialects for the Toplink, Hibernate and OpenJPA JPA implementations. See the next section for details on the JpaDialect mechanism.

13.5.4 JpaDialect

As an advanced feature JpaTemplate, JpaTransactionManager and subclasses of AbstractEntityManagerFactoryBean support a custom JpaDialect, to be passed into the jpaDialect bean property. In such a scenario, the DAOs do not receive an EntityManagerFactory reference but rather a full JpaTemplate instance (for example, passed into the jpaTemplate property of JpaDaoSupport). A JpaDialect implementation can enable some advanced features supported by Spring, usually in a vendor-specific manner:

  • Applying specific transaction semantics such as custom isolation level or transaction timeout)

  • Retrieving the transactional JDBC Connection for exposure to JDBC-based DAOs)

  • Advanced translation of PersistenceExceptions to Spring DataAccessExceptions

This is particularly valuable for special transaction semantics and for advanced translation of exception. The default implementation used (DefaultJpaDialect) does not provide any special capabilities and if the above features are required, you have to specify the appropriate dialect.

See the JpaDialect Javadoc for more details of its operations and how they are used within Spring's JPA support.