JPA

  • LocalEntityManagerFactoryBean:用于简单部署环境,仅使用 JPA 进行数据访问,受到功能限制最大。

  • 从 JNDI 中获取 EntityManagerFactory:用于部署到 Jakarta EE 服务器中,允许从 JNDI 获取 EntityManagerFactory,使用标准的 Jakarta EE 引导。

  • LocalContainerEntityManagerFactoryBean:用于完整 JPA 功能,允许获取 EntityManagerFactory,完全控制其配置,支持与现有 JDBC 配置的链接,本地和全局事务,但对运行时环境和类加载器有要求。

Spring JPA(可在 org.springframework.orm.jpa 包下获取)以类似于与 Hibernate 集成的方式提供对 Java Persistence API 的全面支持,同时了解底层实现以提供其他功能。

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

Spring JPA 支持提供三种方法来设置 JPA EntityManagerFactory,应用程序使用它获取实体管理器。

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.

LocalEntityManagerFactoryBean 会创建一个适合简单部署环境的 EntityManagerFactory,在这些环境中应用程序仅使用 JPA 进行数据访问。工厂 Bean 使用 JPA PersistenceProvider 自动检测机制(根据 JPA 的 Java SE 引导),在大多数情况下,它只需要你指定持久单元名称。以下 XML 示例配置了这样的 Bean:

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>

这种形式的 JPA 部署是最简单且受限最大的。你无法引用现有的 JDBC DataSource Bean 定义,也不支持全局事务。此外,持久类的编译(字节码转换)是特定于提供者的,通常需要在启动时指定特定的 JVM 代理。此选项仅适用于独立应用程序和测试环境,JPA 规范为此而设计。

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

在部署到 Jakarta EE 服务器时可以使用此选项。查看服务器文档,了解如何将自定义 JPA 提供者部署到服务器中,允许使用与服务器默认值不同的提供者。

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.

从 JNDI 获取 EntityManagerFactory(例如在 Jakarta EE 环境中),是一个修改 XML 配置的问题,如以下示例所示:

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>

此操作假设标准 Jakarta EE 引导。Jakarta EE 服务器自动检测持久单元(实际上是应用程序 jar 文件中的 META-INF/persistence.xml 文件)和 Jakarta EE 部署描述符(例如 web.xml)中的 persistence-unit-ref 项,并为这些持久单元定义环境命名上下文位置。

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.

在此场景中,整个持久单元部署,包括持久类的编译(字节码转换),都是由 Jakarta EE 服务器完成的。JDBC DataSource 通过 META-INF/persistence.xml 文件中的 JNDI 位置来定义。EntityManager 事务与服务器的 JTA 子系统集成。Spring 只使用获取的 EntityManagerFactory,通过依赖注入将其传递给应用程序对象,并管理持久单元的事务(通常通过 JtaTransactionManager)。

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).

如果在同一个应用程序中使用多个持久单元,则从 JNDI 检索到的这些持久单元的 Bean 名称应该与应用程序用来引用它们的持久单元名称相匹配(例如,@PersistenceUnit@PersistenceContext 注释)。

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

你可以在基于 Spring 的应用程序环境中使用此选项来获得完整的 JPA 功能。这包括 Web 容器(例如 Tomcat)、独立应用程序以及具有复杂持久性要求的集成测试。

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.

LocalContainerEntityManagerFactoryBean 可以完全控制 EntityManagerFactory 的配置,适用于需要细粒度自定义的环境。LocalContainerEntityManagerFactoryBean 基于 persistence.xml 文件、提供的 dataSourceLookup 策略以及指定的 loadTimeWeaver 创建一个 PersistenceUnitInfo 实例。这样,就可以与 JNDI 之外的自定义数据源配合使用并控制编译过程。以下示例展示了 LocalContainerEntityManagerFactoryBean 的典型 Bean 定义:

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>

以下示例展示了典型的 persistence.xml 文件:

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>

<exclude-unlisted-classes/> 快捷方式表明不会扫描带批注的实体类。显式的“true”值 (<exclude-unlisted-classes>true</exclude-unlisted-classes/>) 也表示不扫描。<exclude-unlisted-classes>false</exclude-unlisted-classes/> 的确会触发扫描。但是,我们建议如果你想要执行实体类扫描,则省略 exclude-unlisted-classes 元素。

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.

使用 LocalContainerEntityManagerFactoryBean 是最强大的 JPA 设置选项,允许在应用程序中进行灵活的本地配置。它支持与现有 JDBC DataSource 的链接,支持本地和全局事务,等等。但是,它也对运行时环境提出要求,例如,如果持久性提供程序要求字节码转换,则需要具有编译支持的类加载器。

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.

此选项可能与 Jakarta EE 服务器的内置 JPA 功能相冲突。在完整的 Jakarta EE 环境中,考虑从 JNDI 获取 EntityManagerFactory。或者,在 LocalContainerEntityManagerFactoryBean 定义(例如 META-INF/my-persistence.xml)上指定一个自定义 persistenceXmlLocation,并在应用程序 jar 文件中只包含带有该名称的描述符。由于 Jakarta EE 服务器只查找默认的 META-INF/persistence.xml 文件,因此它会忽略此类自定义持久单元,从而避免了与 Spring 驱动的 JPA 设置的冲突(例如,适用于 Resin 3.1)。

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. (This applies to Resin 3.1, for example.)

When is load-time weaving required?

并非所有 JPA 提供程序都需要 JVM 代理。Hibernate 就是一个不需要代理的例子。如果你的提供程序不需要代理,或者你有其他备选方案,例如通过自定义编译器或 Ant 任务在构建时应用增强,则不应使用加载时编译器。

Not all JPA providers require a JVM agent. Hibernate is an example of one that does not. If your provider does not require an agent or you have other alternatives, such as applying enhancements at build time through a custom compiler or an Ant task, you should not use the load-time weaver.

LoadTimeWeaver 接口是 Spring 提供的类,它以特定方式插入 JPA ClassTransformer 实例,这取决于环境是 Web 容器还是应用程序服务器。通过一个 代理结合 ClassTransformers 通常效率不高。代理对整个虚拟机起作用并且检查加载的每个类,这在生产服务器环境中通常不受欢迎。

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 为各种环境提供多种 LoadTimeWeaver 实现,让 ClassTransformer 实例只针对每个类加载器应用,而不是每个虚拟机。

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.

请参见 AOP 章节中的 Spring configuration 了解更多关于 LoadTimeWeaver 实现及其设置方面的信息,无论是通用设置还是针对各种平台(如 Tomcat、JBoss 和 WebSphere)的自定义设置。

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).

Spring configuration 中所述,您可以使用 @EnableLoadTimeWeaving 注解或 context:load-time-weaver XML 元素配置一个上下文范围的 LoadTimeWeaver。所有 JPA LocalContainerEntityManagerFactoryBean 实例都会自动选取这样的全局 weaver。以下示例展示了设置一个加载时 weaver 的首选方式,它自动检测平台(如 Tomcat 的支持 weaving 的类加载器或 Spring 的 JVM 代理)和自动将 weaver 传播到所有支持 weaver 的 bean:

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 (e.g. 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>

但是,如有需要,您可以像以下示例所示,通过 loadTimeWeaver 属性手动指定专用的织入器:

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>

无论如何配置 LTW,通过使用此技术,依赖于检测的 JPA 应用程序可以在目标平台(例如 Tomcat)中运行,而不需要代理。当托管应用程序依赖于不同的 JPA 实现时,这一点尤其重要,因为 JPA 转换器仅在类加载器级别上应用,因此彼此隔离。

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

对于依赖于多个持久性单元位置(例如存储在类路径中的各种 JAR 中)的应用程序,Spring 提供了 PersistenceUnitManager 作为中央存储库,以避免持久性单元发现过程,该过程可能是昂贵的。默认实现允许指定多个位置。这些位置将被解析,稍后通过持久性单元名称进行检索。(默认情况下,类路径被搜索 META-INF/persistence.xml 文件。)以下示例配置了多个位置:

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>

默认实现允许自定义 PersistenceUnitInfo 实例(在将它们馈送到 JPA 提供程序之前),可以通过声明方式(通过其属性,影响所有托管单元)或编程方式(通过 PersistenceUnitPostProcessor,它允许持久性单元选择)。如果未指定 PersistenceUnitManager,则由 LocalContainerEntityManagerFactoryBean 创建一个并内部使用该管理器。

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 支持通过 bootstrapExecutor 属性进行后台引导,如下面的示例所示:

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>

将实际的 JPA 提供程序引导转交给指定的执行器,然后与应用程序引导线程并行运行。公开的 EntityManagerFactory 代理可以注入到其他应用程序组件中,甚至可以响应 EntityManagerFactoryInfo 配置检查。但是,一旦其他组件访问了实际的 JPA 提供程序(例如调用 createEntityManager),这些调用会阻塞,直到后台引导完成。特别是,当您使用 Spring Data JPA 时,请确保也为其存储库设置延迟引导。

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.

从 6.2 开始,在上下文刷新完成之前强制执行 JPA 初始化,等待异步引导在此之前完成。这使完全初始化的数据库基础结构的可用性变得可预测,并允许在 ContextRefreshedEvent 侦听器等中进行自定义后初始化逻辑。不建议将此类应用程序级的数据库初始化放入 @PostConstruct 方法或类似方法中;最好将其放在 Lifecycle.start 中(如果适用)或 ContextRefreshedEvent 侦听器中。

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

尽管 EntityManagerFactory 实例是线程安全的,但 EntityManager 实例并非如此。所注入的 JPA EntityManager 的行为就像从应用程序服务器的 JNDI 环境中获取 EntityManager 一样,这是 JPA 规范所定义的。它将所有调用委托给当前的事务 EntityManager(如果有)。否则,它会退回到每个操作新建的 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.

可以通过使用注入的 EntityManagerFactoryEntityManager 编写针对普通 JPA 的代码,而无需任何 Spring 依赖项。如果启用了 PersistenceAnnotationBeanPostProcessor,Spring 可以理解 @PersistenceUnit@PersistenceContext 注释,这些注释位于字段和方法级别。下面的示例展示了一个使用 @PersistenceUnit 注释的普通 JPA DAO 实现:

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;
	}
}

前面的 DAO 并不依赖于 Spring,但仍然很好地适应 Spring 应用程序上下文。此外,DAO 利用注释需要注入默认 EntityManagerFactory,如下面的示例 bean 定义所示:

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>

作为显式定义 PersistenceAnnotationBeanPostProcessor 的替代方法,请考虑在应用程序上下文配置中使用 Spring context:annotation-config XML 元素。这样做会自动注册所有基于注释配置的 Spring 标准后处理器,包括 CommonAnnotationBeanPostProcessor 等。

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>

这种 DAO 的主要问题在于它总是通过工厂创建一个新的 EntityManager。您可以通过请求一个事务性的 EntityManager(也称为“共享 EntityManager”,因为它是一个用于实际事务性 EntityManager 的共享的、线程安全的代理)来避免这种情况,而不是注入工厂。下面的示例展示了如何执行此操作:

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
	}
}

@PersistenceContext 注释有一个可选属性叫做 type,它默认值为 PersistenceContextType.TRANSACTION。您可以使用此默认值接收共享 EntityManager 代理。替代值 PersistenceContextType.EXTENDED 是一种完全不同的事务。这会导致一个所谓的扩展 EntityManager,它不是线程安全的,因此不能在并发访问的组件中使用,例如 Spring 管理的单例 bean。扩展 EntityManager 实例只应在有状态组件中使用,例如驻留在会话中的组件,其中 EntityManager 的生命周期不绑定到当前事务,而是完全取决于应用程序。

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.

Method- and field-level Injection

您可以在类中的字段或方法上应用指示依赖项注入的注释(例如 @PersistenceUnit@PersistenceContext)— 因此,表达式“方法级注入”和“字段级注入”。字段级注释简洁且易于使用,而方法级注释允许对注入的依赖项进行进一步处理。在这两种情况下,成员可见性(公共、受保护或私有)都不重要。

You can apply annotations that indicate dependency injections (such as @PersistenceUnit and @PersistenceContext) on field or methods inside a class — hence the expressions “method-level injection” and “field-level injection”. Field-level annotations are concise and easier to use while method-level annotations allow for further processing of the injected dependency. In both cases, the member visibility (public, protected, or private) does not matter.

类级别的注释呢?

What about class-level annotations?

在 Jakarta EE 平台上,它们用于依赖项声明,而不是用于资源注入。

On the Jakarta EE platform, they are used for dependency declaration and not for resource injection.

注入的 EntityManager 是 Spring 管理的(知道正在进行的事务)。即使新的 DAO 实现使用 EntityManager 而不是 EntityManagerFactory 的方法级注入,但由于使用注释,因此无需更改 bean 定义。

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.

此 DAO 样式的主要优点是它仅依赖于 Java 持久性 API。不需要导入任何 Spring 类。此外,由于了解 JPA 注释,因此 Spring 容器会自动应用注入项。这从非侵入性的角度来看很有吸引力,并且对 JPA 开发人员来说感觉更自然。

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@PersistenceContext 只能声明在方法和字段上。如何通过构造函数和其他 @Autowired 注入点提供 JPA 资源?

@PersistenceUnit and @PersistenceContext can only be declared on methods and fields. What about providing JPA resources via constructors and other @Autowired injection points?

只要目标被定义为一个 bean(例如通过 LocalContainerEntityManagerFactoryBean),就可以通过构造函数和 @Autowired 字段/方法轻松地注入 EntityManagerFactory。注入点按原样与原始 EntityManagerFactory 定义的类型匹配。

EntityManagerFactory can easily be injected via constructors and @Autowired fields/methods as long as the target is defined as a bean, e.g. via LocalContainerEntityManagerFactoryBean. The injection point matches the original EntityManagerFactory definition by type as-is.

但是,默认情况下,@PersistenceContext 样式的共享 EntityManager 引用不可用于常规依赖项注入。为了使其可用于 @Autowired 要求的基于类型的匹配,请考虑定义一个 SharedEntityManagerBean 作为 EntityManagerFactory 定义的伴随组件:

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>

或者,您可以基于 SharedEntityManagerCreator 定义一个 @Bean 方法:

Alternatively, you may define an @Bean method based on SharedEntityManagerCreator:

@Bean("em")
public static EntityManager sharedEntityManager(EntityManagerFactory emf) {
	return SharedEntityManagerCreator.createSharedEntityManager(emf);
}

在存在多个持久化单元的情况下,每个 EntityManagerFactory 定义都需要有相应的 EntityManager bean 定义,最好使用与不同的 EntityManagerFactory 定义相匹配的限定符,以便通过 @Autowired @Qualifier("…​") 区分持久化单元。

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

我们强烈建议您阅读 Declarative Transaction Management,如果您还没有这样做,以便更详细地了解 Spring 的声明式事务支持。

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.

用于 JPA 的建议策略是通过 JPA 的本机事务支持进行本地事务。Spring 的 JpaTransactionManager 提供了许多已知的本地 JDBC 事务的功能(例如特定事务的隔离级别和资源级只读优化),而无需 JTA 事务协调器和支持 XA 的资源。

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 还允许一个已配置的 JpaTransactionManager 将 JPA 事务公开给 JDBC 访问代码,此外,该 JDBC 访问代码会访问相同的 DataSource,前提是已注册的 JpaDialect 支持检索底层的 JDBC Connection。Spring 为 EclipseLink 和 Hibernate JPA 实现提供了方言。请参见 next section 了解更多 JpaDialect 细节。

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.

对于实际资源连接的 JTA 样式延迟检索,Spring 为目标连接池提供了相应的 DataSource 代理类:请参阅 LazyConnectionDataSourceProxy。这对于 JPA 只读事务特别有用,该事务通常可以从本地缓存中进行处理,而不是命中数据库。

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

作为一项高级功能,JpaTransactionManagerAbstractEntityManagerFactoryBean 的子类允许将自定义 JpaDialect 传递到 jpaDialect bean 属性中。JpaDialect 实现通常以特定于供应商的方式启用 Spring 支持的以下高级功能:

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 PersistenceException to Spring’s DataAccessException

这对于特殊的的事务语义和高级的异常转换特别有价值。默认实现 (DefaultJpaDialect) 不提供任何特殊功能,如果需要前面列出的功能,则必须指定适当的方言。

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.

作为一个最初主要用于 Spring 全功能 LocalContainerEntityManagerFactoryBean 设置的更广泛的提供程序适应设施,JpaVendorAdapterJpaDialect 的能力与其他特定于提供程序的默认值结合在一起。指定 HibernateJpaVendorAdapterEclipseLinkJpaVendorAdapter 是分别为 Hibernate 或 EclipseLink 自动配置一个 EntityManagerFactory 设置的最便捷方式。请注意,这些提供程序适配器主要设计用于与 Spring 驱动的交易管理配合使用(即与 JpaTransactionManager 一起使用)。

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).

参阅 JpaDialectJpaVendorAdapter javadoc 以获得更多有关其操作的详细信息以及如何在 Spring 的 JPA 支持中使用它们。

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

作为 JpaTransactionManager 的替代方法,Spring 还允许通过 JTA 进行多资源事务协调,无论是在 Jakarta EE 环境中还是在独立的事务协调器(如 Atomikos)中。除了选择 Spring 的 JtaTransactionManager 而不是 JpaTransactionManager 之外,您还需要采取一些进一步的步骤:

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 DataSource through JNDI. See your application server documentation for details. Analogously, a standalone transaction coordinator usually comes with special XA-integrated DataSource variants. Again, check its documentation.

  • The JPA EntityManagerFactory setup needs to be configured for JTA. This is provider-specific, typically through special properties to be specified as jpaProperties on LocalContainerEntityManagerFactoryBean. In the case of Hibernate, these properties are even version-specific. See your Hibernate documentation for details.

  • Spring’s HibernateJpaVendorAdapter enforces 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’s hibernate.connection.handling_mode property to DELAYED_ACQUISITION_AND_RELEASE_AFTER_STATEMENT to restore Hibernate’s own default. See Spurious Application Server Warnings with Hibernate for related notes.

  • Alternatively, consider obtaining the EntityManagerFactory from your application server itself (that is, through a JNDI lookup instead of a locally declared LocalContainerEntityManagerFactoryBean). A server-provided EntityManagerFactory might 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

HibernateTransactionManager 结合使用的本机 LocalSessionFactoryBean 设置允许与 @PersistenceContext 和其他 JPA 访问代码进行交互。现在,Hibernate SessionFactory 本机实现了 JPA 的 EntityManagerFactory 接口,Hibernate Session 句柄本机是 JPA EntityManager。Spring 的 JPA 支持工具会自动检测本机 Hibernate 会话。

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.

因此,这种本机 Hibernate 设置可以在许多场景中替代标准 JPA LocalContainerEntityManagerFactoryBeanJpaTransactionManager 组合,允许在同一本地事务中使用 SessionFactory.getCurrentSession()(以及 HibernateTemplate)与 @PersistenceContext EntityManager 进行交互。这种设置还提供了更强的 Hibernate 集成和更高的配置灵活性,因为它不受 JPA 引导程序合约的约束。

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.

在这种情况下,您不需要 HibernateJpaVendorAdapter 配置,因为 Spring 的本机 Hibernate 设置提供了更多功能(例如,自定义 Hibernate 集成器设置、Hibernate 5.3 bean 容器集成以及对只读事务的更强大的优化)。最后但并非最不重要的一点,您还可以通过 LocalSessionFactoryBuilder 表达本机 Hibernate 设置,与 @Bean 样式配置无缝集成(不涉及 FactoryBean)。

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).

LocalSessionFactoryBeanLocalSessionFactoryBuilder 支持后台自举程序,就像 JPA LocalContainerEntityManagerFactoryBean 一样。请参见 Background Bootstrapping 了解简介。

LocalSessionFactoryBean and LocalSessionFactoryBuilder support background bootstrapping, just as the JPA LocalContainerEntityManagerFactoryBean does. See Background Bootstrapping for an introduction.

LocalSessionFactoryBean 上,可以通过 bootstrapExecutor 属性获得此功能。在编程 LocalSessionFactoryBuilder 上,重载的 buildSessionFactory 方法采用引导执行器参数。

On LocalSessionFactoryBean, this is available through the bootstrapExecutor property. On the programmatic LocalSessionFactoryBuilder, an overloaded buildSessionFactory method takes a bootstrap executor argument.