JPA
The Spring JPA, available under the org.springframework.orm.jpa package, offers
comprehensive support for the
Java Persistence
API in a manner similar to the integration with Hibernate while being aware of
the underlying implementation in order to provide additional features.
Three Options for JPA Setup in a Spring Environment
The Spring JPA support offers three ways of setting up the JPA EntityManagerFactory
that is used by the application to obtain an entity manager.
Using LocalEntityManagerFactoryBean
You can use this option only 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 auto-detection mechanism (according
to JPA’s Java SE bootstrapping) and, in most cases, requires you to specify only the
persistence unit name. The following XML example configures such a bean:
<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 be specified on startup. This
option is sufficient only for stand-alone applications and test environments, for which
the JPA specification is designed.
Obtaining an EntityManagerFactory from JNDI
You can use this option when deploying to a Jakarta EE 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 Jakarta EE environment),
is a matter of changing the XML configuration, as the following example shows:
<beans>
<jee:jndi-lookup id="myEmf" jndi-name="persistence/myPersistenceUnit"/>
</beans>
This action assumes standard Jakarta EE bootstrapping. The Jakarta EE server auto-detects
persistence units (in effect, META-INF/persistence.xml files in application jars) and
persistence-unit-ref entries in the Jakarta 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 Jakarta 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 you use multiple persistence units 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).
Using LocalContainerEntityManagerFactoryBean
You can use this option for full JPA capabilities in a Spring-based application environment. This includes web containers such as Tomcat, 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 instance based 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>
The <exclude-unlisted-classes/> shortcut indicates that no scanning for
annotated entity classes is supposed to occur. An explicit 'true' value
(<exclude-unlisted-classes>true</exclude-unlisted-classes/>) also means no scan.
<exclude-unlisted-classes>false</exclude-unlisted-classes/> does trigger a scan.
However, we recommend omitting 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 Jakarta EE server. In a
full Jakarta EE environment, consider obtaining your EntityManagerFactory from JNDI.
Alternatively, specify a custom persistenceXmlLocation on your
LocalContainerEntityManagerFactoryBean definition (for example,
META-INF/my-persistence.xml) and include only a descriptor with that name in your
application jar files. Because the Jakarta EE server looks only for default
META-INF/persistence.xml files, it ignores such custom persistence units and, hence,
avoids conflicts with a Spring-driven JPA setup upfront.
The LoadTimeWeaver interface is a Spring-provided class that lets JPA
ClassTransformer instances be plugged in a specific manner, depending on whether the
environment is a web container or application server. Hooking ClassTransformers
through an
agent
is typically 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,
letting ClassTransformer instances be applied only for each class loader and not
for each VM.
See the Spring configuration in the AOP chapter for
more insight regarding the LoadTimeWeaver implementations and their setup, either
generic or customized to various platforms (such as Tomcat, JBoss and WebSphere).
As described in Spring configuration, you can configure
a context-wide LoadTimeWeaver by using the @EnableLoadTimeWeaving annotation or the
context:load-time-weaver XML element. Such a global weaver is automatically picked up
by all JPA LocalContainerEntityManagerFactoryBean instances. The following example
shows the preferred way of setting up a load-time weaver, delivering auto-detection
of the platform (for example, Tomcat’s weaving-capable class loader or Spring’s JVM 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>
However, you can, if needed, manually specify a dedicated weaver through the
loadTimeWeaver property, as the following example shows:
<bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="loadTimeWeaver">
<bean class="org.springframework.instrument.classloading.ReflectiveLoadTimeWeaver"/>
</property>
</bean>
No matter how the LTW is configured, by using this technique, JPA applications relying on instrumentation can run in the target platform (for example, Tomcat) without needing an agent. This is especially important when the hosting applications rely on different JPA implementations, because the JPA transformers are applied only at the class-loader level and are, thus, isolated from each other.
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 lets multiple locations be specified. These locations are
parsed and later retrieved through the persistence unit name. (By default, the classpath
is searched for META-INF/persistence.xml files.) The following example configures
multiple locations:
<bean id="pum" class="org.springframework.orm.jpa.persistenceunit.DefaultPersistenceUnitManager">
<property name="persistenceXmlLocations">
<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"/>
<property name="persistenceUnitName" value="myCustomUnit"/>
</bean>
The default implementation allows customization of the PersistenceUnitInfo instances
(before they are fed to the JPA provider) either 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.
Background Bootstrapping
LocalContainerEntityManagerFactoryBean supports background bootstrapping through
the bootstrapExecutor property, as the following example shows:
<bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="bootstrapExecutor">
<bean class="org.springframework.core.task.SimpleAsyncTaskExecutor"/>
</property>
</bean>
The actual JPA provider bootstrapping is handed off to the specified executor and then,
running in parallel, to the application bootstrap thread. The exposed EntityManagerFactory
proxy can be injected into other application components and is even able to respond to
EntityManagerFactoryInfo configuration inspection. However, once the actual JPA provider
is being accessed by other components (for example, calling createEntityManager), those
calls block until the background bootstrapping has completed. In particular, when you use
Spring Data JPA, make sure to set up deferred bootstrapping for its repositories as well.
As of 6.2, JPA initialization is enforced before context refresh completion, waiting for
asynchronous bootstrapping to complete by then. This makes the availability of the fully
initialized database infrastructure predictable and allows for custom post-initialization
logic in ContextRefreshedEvent listeners etc. Putting such application-level database
initialization into @PostConstruct methods or the like is not recommended; this is
better placed in Lifecycle.start (if applicable) or a ContextRefreshedEvent listener.
Implementing DAOs Based on JPA: EntityManagerFactory and EntityManager
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 the
@PersistenceUnit and @PersistenceContext annotations both at the field and the method
level if a PersistenceAnnotationBeanPostProcessor is enabled. The following example
shows a plain JPA DAO implementation that uses the @PersistenceUnit annotation:
-
Java
-
Kotlin
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();
}
}
}
}
class ProductDaoImpl : ProductDao {
private lateinit var emf: EntityManagerFactory
@PersistenceUnit
fun setEntityManagerFactory(emf: EntityManagerFactory) {
this.emf = emf
}
fun loadProductsByCategory(category: String): Collection<*> {
val em = this.emf.createEntityManager()
val query = em.createQuery("from Product as p where p.category = ?1");
query.setParameter(1, category);
return query.resultList;
}
}
The preceding DAO 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, as the following example bean definition shows:
<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 explicitly defining a PersistenceAnnotationBeanPostProcessor,
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.
Consider the following example:
<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 a
“shared EntityManager” because it is a shared, thread-safe proxy for the actual transactional
EntityManager) to be injected instead of the factory. The following example shows how to do so:
-
Java
-
Kotlin
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();
}
}
class ProductDaoImpl : ProductDao {
@PersistenceContext
private lateinit var em: EntityManager
fun loadProductsByCategory(category: String): Collection<*> {
val query = em.createQuery("from Product as p where p.category = :category")
query.setParameter("category", category)
return query.resultList
}
}
The @PersistenceContext annotation has an optional attribute called type, which defaults
to PersistenceContextType.TRANSACTION. You can use this default 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 EntityManager instances 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).
Even though the new DAO implementation uses method-level injection of an EntityManager
instead of an EntityManagerFactory, no change is required in the bean definition
due to annotation usage.
The main advantage of this DAO style is that it depends only on the 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 can feel more natural to JPA developers.
Implementing DAOs Based on @Autowired (typically with constructor-based injection)
@PersistenceUnit and @PersistenceContext can only be declared on methods and fields.
What about providing JPA resources via constructors and other @Autowired injection points?
EntityManagerFactory can easily be injected via constructors and @Autowired fields/methods
as long as the target is defined as a bean, for example, via LocalContainerEntityManagerFactoryBean.
The injection point matches the original EntityManagerFactory definition by type as-is.
However, an @PersistenceContext-style shared EntityManager reference is not available for
regular dependency injection out of the box. In order to make it available for type-based
matching as required by @Autowired, consider defining a SharedEntityManagerBean as a
companion for your EntityManagerFactory definition:
<bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
...
</bean>
<bean id="em" class="org.springframework.orm.jpa.support.SharedEntityManagerBean">
<property name="entityManagerFactory" ref="emf"/>
</bean>
Alternatively, you may define an @Bean method based on SharedEntityManagerCreator:
@Bean("em")
public static EntityManager sharedEntityManager(EntityManagerFactory emf) {
return SharedEntityManagerCreator.createSharedEntityManager(emf);
}
In case of multiple persistence units, each EntityManagerFactory definition needs to be
accompanied by a corresponding EntityManager bean definition, ideally with qualifiers
that match with the distinct EntityManagerFactory definition in order to distinguish
the persistence units via @Autowired @Qualifier("…").
Spring-driven JPA Transactions
| We strongly encourage you to read Declarative Transaction Management, if you have not already done so, to get more detailed coverage of Spring’s declarative transaction support. |
The recommended strategy for JPA is local transactions through JPA’s native transaction
support. Spring’s JpaTransactionManager provides many capabilities known from local
JDBC transactions (such as transaction-specific isolation levels and resource-level
read-only optimizations) against any regular JDBC connection pool, without requiring
a JTA transaction coordinator and XA-capable resources.
Spring JPA also lets a configured JpaTransactionManager expose a JPA transaction
to JDBC access code that accesses the same DataSource, provided that the registered
JpaDialect supports retrieval of the underlying JDBC Connection. Spring provides
dialects for the EclipseLink and Hibernate JPA implementations. See the
next section for details on JpaDialect.
For JTA-style lazy retrieval of actual resource connections, Spring provides a
corresponding DataSource proxy class for the target connection pool: see
LazyConnectionDataSourceProxy.
This is particularly useful for JPA read-only transactions which can often
be processed from a local cache rather than hitting the database.
Understanding JpaDialect and JpaVendorAdapter
As an advanced feature, JpaTransactionManager and subclasses of
AbstractEntityManagerFactoryBean allow a custom JpaDialect to be passed into the
jpaDialect bean property. A JpaDialect implementation can enable the following 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
PersistenceExceptionto Spring’sDataAccessException
This is particularly valuable for special transaction semantics and for advanced
translation of exception. The default implementation (DefaultJpaDialect) does
not provide any special abilities and, if the features listed earlier are required, you have
to specify the appropriate dialect.
As an even broader provider adaptation facility primarily for Spring’s full-featured
LocalContainerEntityManagerFactoryBean setup, JpaVendorAdapter combines the
capabilities of JpaDialect with other provider-specific defaults. Specifying a
HibernateJpaVendorAdapter or EclipseLinkJpaVendorAdapter is the most convenient
way of auto-configuring an EntityManagerFactory setup for Hibernate or EclipseLink,
respectively. Note that those provider adapters are primarily designed for use with
Spring-driven transaction management (that is, for use with JpaTransactionManager).
|
See the JpaDialect and
JpaVendorAdapter javadoc for
more details of its operations and how they are used within Spring’s JPA support.
Setting up JPA with JTA Transaction Management
As an alternative to JpaTransactionManager, Spring also allows for multi-resource
transaction coordination through JTA, either in a Jakarta EE environment or with a
stand-alone transaction coordinator, such as Atomikos. Aside from choosing Spring’s
JtaTransactionManager instead of JpaTransactionManager, you need to take few further
steps:
-
The underlying JDBC connection pools need to be XA-capable and be integrated with your transaction coordinator. This is usually straightforward in a Jakarta EE environment, exposing a different kind of
DataSourcethrough JNDI. See your application server documentation for details. Analogously, a standalone transaction coordinator usually comes with special XA-integratedDataSourcevariants. Again, check its documentation. -
The JPA
EntityManagerFactorysetup needs to be configured for JTA. This is provider-specific, typically through special properties to be specified asjpaPropertiesonLocalContainerEntityManagerFactoryBean. In the case of Hibernate, these properties are even version-specific. See your Hibernate documentation for details. -
Spring’s
HibernateJpaVendorAdapterenforces certain Spring-oriented defaults, such as the connection release mode,on-close, which matches Hibernate’s own default in Hibernate 5.0 but not any more in Hibernate 5.1+. For a JTA setup, make sure to declare your persistence unit transaction type as "JTA". Alternatively, set Hibernate 5.2’shibernate.connection.handling_modeproperty toDELAYED_ACQUISITION_AND_RELEASE_AFTER_STATEMENTto restore Hibernate’s own default. See Spurious Application Server Warnings with Hibernate for related notes. -
Alternatively, consider obtaining the
EntityManagerFactoryfrom your application server itself (that is, through a JNDI lookup instead of a locally declaredLocalContainerEntityManagerFactoryBean). A server-providedEntityManagerFactorymight require special definitions in your server configuration (making the deployment less portable) but is set up for the server’s JTA environment.
Native Hibernate Setup and Native Hibernate Transactions for JPA Interaction
A native LocalSessionFactoryBean setup in combination with HibernateTransactionManager
allows for interaction with @PersistenceContext and other JPA access code. A Hibernate
SessionFactory natively implements JPA’s EntityManagerFactory interface now
and a Hibernate Session handle natively is a JPA EntityManager.
Spring’s JPA support facilities automatically detect native Hibernate sessions.
Such native Hibernate setup can, therefore, serve as a replacement for a standard JPA
LocalContainerEntityManagerFactoryBean and JpaTransactionManager combination
in many scenarios, allowing for interaction with SessionFactory.getCurrentSession()
(and also HibernateTemplate) next to @PersistenceContext EntityManager within
the same local transaction. Such a setup also provides stronger Hibernate integration
and more configuration flexibility, because it is not constrained by JPA bootstrap contracts.
You do not need HibernateJpaVendorAdapter configuration in such a scenario,
since Spring’s native Hibernate setup provides even more features
(for example, custom Hibernate Integrator setup, Hibernate 5.3 bean container integration,
and stronger optimizations for read-only transactions). Last but not least, you can also
express native Hibernate setup through LocalSessionFactoryBuilder,
seamlessly integrating with @Bean style configuration (no FactoryBean involved).
|
On |