# 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方法采用引导执行程序参数。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.flydean.com/spring-framework-documentation5/dataaccess/4.0orm/4.4jpa.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
