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.
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.
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.
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.
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 | |
---|---|
The |
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.
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:
Copy spring-tomcat-weaver.jar
into
$CATALINA_HOME/server/lib,where
$CATALINA_HOME represents the root of the
Tomcat installation.
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>
Copy spring-tomcat-weaver.jar
into
$CATALINA_HOME/lib, where
$CATALINA_HOME represents the root of the
Tomcat installation)
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 | |
---|---|
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. |
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
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.
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
.
Note | |
---|---|
Although |
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.
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.
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.