> For the complete documentation index, see [llms.txt](https://docs.flydean.com/spring-framework-documentation5/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.flydean.com/spring-framework-documentation5/dataaccess/4.0orm/4.4jpa.md).

# 4.4JPA

可以在org.springframework.orm.jpa包下获得的Spring JPA以类似于与Hibernate集成的方式，为Java Persistence API提供了全面的支持，同时使用到了底层实现以提供附加功能。

## 4.4.1 在Spring环境中设置JPA的三个选项

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

* 使用LocalEntityManagerFactoryBean
* 从JNDI获取EntityManagerFactory
* 使用LocalContainerEntityManagerFactoryBean

### 使用LocalEntityManagerFactoryBean

您只能在简单的部署环境（例如独立应用程序和集成测试）中使用此选项。

LocalEntityManagerFactoryBean创建一个EntityManagerFactory，适用于应用程序仅使用JPA进行数据访问的简单部署环境。 工厂bean使用JPA PersistenceProvider自动检测机制（根据JPA的Java SE自举），并且在大多数情况下，仅要求您指定持久性单元名称。 以下XML示例配置了这样的bean：

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

这种形式的JPA部署是最简单和最有限的。 您不能引用现有的JDBC DataSource bean定义，并且不存在对全局事务的支持。 此外，持久类的编织（字节码转换）是特定于提供程序的，通常需要在启动时指定特定的JVM代理。 该选项仅对于设计了JPA规范的独立应用程序和测试环境就足够了。

### 从JNDI获取EntityManagerFactory

部署到Java EE服务器时，可以使用此选项。 查看服务器的文档，以了解如何将自定义JPA提供程序部署到服务器中，以允许使用与服务器默认设置不同的提供程序。

从JNDI获得EntityManagerFactory（例如在Java EE环境中）是更改XML配置的问题，如以下示例所示：

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

此操作假定标准Java EE引导。 Java EE服务器自动检测Java EE部署描述符中的持久性单元（实际上是应用程序jar中的META-INF / persistence.xml文件）和persistence-unit-ref条目，并定义环境命名这些持久性单元的上下文位置。

在这种情况下，整个持久性单元部署，包括持久性类的编织（字节码转换），都取决于Java EE服务器。 JDBC数据源是通过META-INF / persistence.xml文件中的JNDI位置定义的。 EntityManager事务与服务器的JTA子系统集成在一起。 Spring仅使用获得的EntityManagerFactory，通过依赖关系注入将其传递给应用程序对象，并管理持久性单元的事务（通常通过JtaTransactionManager）。

如果在同一应用程序中使用多个持久性单元，则此类JNDI检索的持久性单元的bean名称应与应用程序用来引用它们的持久性单元名称匹配（例如，在@PersistenceUnit和@PersistenceContext批注中）。

使用LocalContainerEntityManagerFactoryBean 您可以在基于Spring的应用程序环境中将此选项用于完整的JPA功能。这包括Web容器（例如Tomcat），独立应用程序以及具有复杂持久性要求的集成测试。

> 如果要专门配置Hibernate设置，则直接的替代方法是使用Hibernate 5.2或5.3并设置本机Hibernate LocalSessionFactoryBean而不是纯JPA LocalContainerEntityManagerFactoryBean，从而使其与JPA访问代码以及本机Hibernate访问代码交互。 有关详细信息，请参见用于JPA交互的本机Hibernate设置。

LocalContainerEntityManagerFactoryBean可以完全控制EntityManagerFactory的配置，适用于需要细粒度自定义的环境。 LocalContainerEntityManagerFactoryBean基于persistence.xml文件，提供的dataSourceLookup策略和指定的loadTimeWeaver创建一个PersistenceUnitInfo实例。 因此，可以在JNDI之外使用自定义数据源并控制编织过程。 以下示例显示了LocalContainerEntityManagerFactoryBean的典型bean定义：

```markup
<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文件：

```markup
<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-unlist-classes> true \</ exclude-unlist-classes />）也意味着不进行扫描。 \<exclude-unlisted-classes> false \</ exclude-unlisted-classes />会触发扫描。但是，如果您希望进行实体类扫描，我们建议省略exclude-unlisted-classes元素。

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

此选项可能与Java EE服务器的内置JPA功能冲突。在完整的Java EE环境中，请考虑从JNDI获取您的EntityManagerFactory。或者，在LocalContainerEntityManagerFactoryBean定义上指定自定义persistenceXmlLocation（例如，META-INF / my-persistence.xml），并在应用程序jar文件中仅包含具有该名称的描述符。由于Java EE服务器仅查找默认的META-INF / persistence.xml文件，因此它会忽略此类自定义持久性单元，因此避免了与Spring预先驱动的JPA设置发生冲突。 （例如，这适用于Resin 3.1。）

> 什么时候需要加载时编织？
>
> 并非所有的JPA提供程序都需要JVM代理。Hibernate是一个没有的例子。如果您的提供程序不需要代理，或者您还有其他选择，例如在构建时通过自定义编译器或Ant任务应用增强功能，则不应使用加载时织布器。

LoadTimeWeaver接口是Spring提供的类，该类使JPA ClassTransformer实例可以以特定方式插入，具体取决于环境是Web容器还是应用程序服务器。通过代理连接ClassTransformers通常效率不高。代理针对整个虚拟机工作，并检查每个已加载的类，这在生产服务器环境中通常是不希望的。

Spring为各种环境提供了许多LoadTimeWeaver实现，让ClassTransformer实例仅应用于每个类加载器，而不应用于每个VM。

请参阅AOP章节中的Spring配置以获取有关LoadTimeWeaver实现及其设置的更多信息，这些实现是通用的或针对各种平台（例如Tomcat，JBoss和WebSphere）定制的。

如Spring配置中所述，您可以通过使用context：load-time-weaver XML元素的@EnableLoadTimeWeaving批注来配置上下文范围的LoadTimeWeaver。所有JPA LocalContainerEntityManagerFactoryBean实例都会自动选择此类全局编织器。下面的示例显示了设置加载时间编织器，提供对平台的自动检测（例如Tomcat的具有编织功能的类加载器或Spring的JVM代理）以及将编织器自动传播到所有可感知编织器的bean的首选方法：

```markup
<context:load-time-weaver/>
<bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    ...
</bean>
```

但是，您可以根据需要通过loadTimeWeaver属性手动指定专用的编织器，如以下示例所示：

```markup
<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转换器仅在类加载器级别应用，因此彼此隔离。

### 处理多个持久性单元

对于依赖多个持久性单元位置的应用程序（例如，存储在类路径中的各种JARS中），Spring提供了PersistenceUnitManager充当中央存储库，并避免了持久性单元发现过程，这可能会很昂贵。 默认实现允许指定多个位置。 解析这些位置，然后通过持久性单元名称进行检索。 （默认情况下，在类路径中搜索META-INF / persistence.xml文件。）以下示例配置多个位置：

```markup
<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>
```

默认实现允许以声明方式（通过影响所有托管单元的属性）或以编程方式（通过允许选择持久单元的PersistenceUnitPostProcessor）自定义PersistenceUnitInfo实例（在将其馈送到JPA提供程序之前）。 如果未指定PersistenceUnitManager，则LocalContainerEntityManagerFactoryBean将创建一个内部使用的对象。

### 后台引导

LocalContainerEntityManagerFactoryBean通过bootstrapExecutor属性支持后台引导，如以下示例所示：

```markup
<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时，请确保还为其存储库设置了延迟引导。

## 4.4.2 基于JPA的DAO实现：EntityManagerFactory和EntityManager

> 尽管EntityManagerFactory实例是线程安全的，但EntityManager实例不是。 注入的JPA EntityManager的行为就像是从应用程序服务器的JNDI环境中提取的EntityManager一样，如JPA规范所定义。 它将所有调用委派给当前的事务性EntityManager（如果有）。 否则，它会退回到每个操作新创建的EntityManager上，实际上使它的使用成为线程安全的。

通过使用注入的EntityManagerFactory或EntityManager，可以在没有任何Spring依赖项的情况下针对普通JPA编写代码。 如果启用了PersistenceAnnotationBeanPostProcessor，Spring可以在字段和方法级别上了解@PersistenceUnit和@PersistenceContext批注。 以下示例显示了使用@PersistenceUnit批注的普通JPA DAO实现：

```java
public class ProductDaoImpl implements ProductDao {

    private EntityManagerFactory emf;

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

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

前面的DAO不依赖于Spring，并且仍然非常适合Spring应用程序上下文。 此外，DAO利用注解的优势要求注入默认的EntityManagerFactory，如以下示例bean定义所示：

```markup
<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等。

考虑以下示例：

```markup
<beans>

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

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

</beans>
```

这种DAO的主要问题在于，它总是在工厂中始终创建一个新的EntityManager。 您可以通过请求注入事务性EntityManager（也称为“共享的EntityManager”，因为它是实际事务性EntityManager的共享的线程安全代理）来代替工厂注入，从而避免了这种情况。 以下示例显示了如何执行此操作：

```java
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();
    }
}
```

@PersistenceContext批注具有一个称为type的可选属性，默认为PersistenceContextType.TRANSACTION。 您可以使用此默认值来接收共享的EntityManager代理。 替代方案PersistenceContextType.EXTENDED是完全不同的事情。 这导致了所谓的扩展EntityManager，它不是线程安全的，因此不能在并发访问的组件（例如Spring管理的单例bean）中使用。 扩展的EntityManager实例仅应用于有状态的组件（例如，驻留在会话中），并且EntityManager的生命周期不依赖于当前事务，而完全取决于应用程序。

> 方法级和字段级注入
>
> 您可以在类中的字段或方法上应用指示依赖项注入的注解（例如@PersistenceUnit和@PersistenceContext），因此表达式为“方法级注入”和“字段级注入”。字段级注解简洁明了，易于使用，而方法级注解则允许对注入的依赖项进行进一步处理。在这两种情况下，成员的可见性（公共，受保护或私有）都无关紧要。
>
> 那类级别的注解呢？
>
> 在Java EE平台上，它们用于依赖性声明，而不用于资源注入。

注入的EntityManager是Spring管理的（意识到正在进行的事务）。即使新的DAO实现使用EntityManager而不是EntityManagerFactory的方法级注入，由于注解的使用，应用程序上下文XML也不需要更改。

这种DAO样式的主要优点是它仅取决于Java Persistence API。不需要导入任何Spring类。此外，由于可以理解JPA批注，因此Spring容器会自动应用注入。从非侵入性的角度来看，这是有吸引力的，并且对于JPA开发人员而言，感觉会更自然。

### 4.4.3 Spring驱动的JPA交易

> 强烈建议您阅读声明式事务管理（如果尚未阅读），以更详细地介绍Spring的声明式事务支持。

对于JPA，推荐的策略是通过JPA的本地交易支持进行本地交易。 Spring的JpaTransactionManager提供了许多本地JDBC事务中已知的功能（例如，特定于事务的隔离级别和资源级别的只读优化），而不需要任何常规JDBC连接池（无需XA）。

如果注册的JpaDialect支持检索基础JDBC连接，Spring JPA还允许已配置的JpaTransactionManager向访问同一数据源的JDBC访问代码公开JPA事务。 Spring为EclipseLink和Hibernate JPA实现提供了方言。有关JpaDialect机制的详细信息，请参见下一部分。

> 作为一种直接替代方案，Spring的本机HibernateTransactionManager能够与Spring Framework 5.1和Hibernate 5.2 / 5.3以及更高版本的JPA访问代码进行交互，以适应多种Hibernate规范并提供JDBC交互。结合LocalSessionFactoryBean设置，这特别有意义。有关详细信息，请参见用于JPA交互的本机Hibernate设置。

### 4.4.4 了解JpaDialect和JpaVendorAdapter

作为高级功能，JpaTransactionManager和AbstractEntityManagerFactoryBean的子类允许将自定义JpaDialect传递到jpaDialect Bean属性中。 JpaDialect实现通常可以通过特定于供应商的方式来启用Spring支持的以下高级功能：

* 应用特定的事务语义（例如自定义隔离级别或事务超时）
* 检索事务性JDBC连接（用于公开基于JDBC的DAO）
* 将PersistenceExceptions高级转换为Spring DataAccessExceptions

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

> 作为主要用于Spring的全功能LocalContainerEntityManagerFactoryBean设置的更广泛的提供程序适应工具，JpaVendorAdapter将JpaDialect的功能与其他特定于提供程序的默认值结合在一起。指定HibernateJpaVendorAdapter或EclipseLinkJpaVendorAdapter分别是为Hibernate或EclipseLink自动配置EntityManagerFactory设置的最便捷方法。请注意，这些提供程序适配器主要是设计用于与Spring驱动的事务管理一起使用的（即，与JpaTransactionManager一起使用）。

有关其操作以及在Spring的JPA支持中如何使用它们的更多详细信息，请参见JpaDialect和JpaVendorAdapter javadoc。

### 4.4.5 使用JTA事务管理设置JPA

作为JpaTransactionManager的替代，Spring还允许通过JTA在Java EE环境中或与独立事务协调器（例如Atomikos）一起进行多资源事务协调。除了选择Spring的JtaTransactionManager而不是JpaTransactionManager之外，您还需要采取其他一些步骤：

* 基础的JDBC连接池需要具有XA功能，并且必须与事务协调器集成在一起。这在Java EE环境中通常很简单，它通过JNDI公开了另一种数据源。有关详细信息，请参见您的应用程序服务器文档。类似地，一个独立的事务协调器通常带有特殊的XA集成的DataSource实现。再次，检查其文档。
* 需要为JTA配置JPA EntityManagerFactory设置。这是特定于提供程序的，通常是通过将特殊属性指定为LocalContainerEntityManagerFactoryBean上的jpaProperties。对于Hibernate，这些属性甚至是特定于版本的。有关详细信息，请参见Hibernate文档。
* Spring的HibernateJpaVendorAdapter强制执行某些面向Spring的默认值，例如连接释放模式，即合上，该模式与Hibernate 5.0中Hibernate自己的默认值匹配，但在5.1 / 5.2中则不再适用。对于JTA设置，请不要声明HibernateJpaVendorAdapter开头或关闭其prepareConnection标志。或者，将Hibernate 5.2的hibernate.connection.handling\_mode属性设置为DELAYED\_ACQUISITION\_AND\_RELEASE\_AFTER\_STATEMENT以恢复Hibernate自己的默认设置。有关WebLogic的相关说明，请参见带有Hibernate的Spurious Application Server警告。
* 或者，考虑从应用程序服务器本身（即通过JNDI查找而不是本地声明的LocalContainerEntityManagerFactoryBean）获取EntityManagerFactory。服务器提供的EntityManagerFactory可能在服务器配置中需要特殊定义（使部署的可移植性降低），但已针对服务器的JTA环境进行了设置。

## 4.4.6 JPA交互的本机Hibernate设置和本机Hibernate事务

从Spring Framework 5.1和Hibernate 5.2 / 5.3开始，与HibernateTransactionManager结合使用的本地LocalSessionFactoryBean设置允许与@PersistenceContext和其他JPA访问代码进行交互。现在，Hibernate SessionFactory本地实现JPA的EntityManagerFactory接口，而Hibernate Session句柄本身就是JPA EntityManager。 Spring的JPA支持工具会自动检测本地的Hibernate会话。

因此，在许多情况下，这种本地Hibernate设置可以替代标准JPA LocalContainerEntityManagerFactoryBean和JpaTransactionManager组合，从而允许在同一本地事务中与@PersistenceContext EntityManager旁边的SessionFactory.getCurrentSession（）（以及HibernateTemplate）进行交互。这样的设置还提供了更强的Hibernate集成和更大的配置灵活性，因为它不受JPA引导合同的约束。

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

就像JPA LocalContainerEntityManagerFactoryBean一样，LocalSessionFactoryBean和LocalSessionFactoryBuilder支持后台引导。有关简介，请参见后台引导。

在LocalSessionFactoryBean上，可以通过bootstrapExecutor属性使用。在编程的LocalSessionFactoryBuilder上，重载的buildSessionFactory方法采用引导执行程序参数。
