20. Object 关系映射(ORM)数据访问

20.1 ORM 简介 Spring

Spring Framework 支持 integration 与 Hibernate,Java Persistence API(JPA)和 Java Data Objects(JDO)进行资源管理,数据访问对象(DAO)Implementations 和 transaction 策略。例如,对于 Hibernate,有 first-class 支持几个方便的 IoC features 来解决许多典型的 Hibernate integration 问题。您可以通过依赖注入为 O/R(object 关系)映射工具配置所有支持的 features。他们可以参与 Spring 的资源和 transaction management,它们符合 Spring 的通用 transaction 和 DAO exception 层次结构。推荐的 integration 样式是针对普通的 Hibernate,JPA 和 JDO API 来编码 DAO。不再推荐使用 Spring 的 DAO 模板的较旧风格;但是,这种风格的覆盖范围可以在附录的第 39.1 节,“经典 ORM 用法”中找到。

当您创建数据访问应用程序时,Spring 会为您选择的 ORM 层添加重要的增强功能。您可以根据需要利用尽可能多的 integration 支持,并且您应该将此集成工作与构建类似基础架构的成本和风险进行比较 in-house。您可以像使用 library 一样使用大部分 ORM 支持,无论技术如何,因为所有内容都设计为一组可重用的 JavaBeans。 Spring IoC 容器中的 ORM 有助于配置和部署。因此,本节中的大多数示例都显示了 Spring 容器中的 configuration。

使用 Spring Framework 创建 ORM DAO 的好处包括:

  • 更容易测试。 Spring 的 IoC 方法可以轻松交换 Hibernate SessionFactory实例,JDBC DataSource实例,transaction managers 和映射 object implementations(如果需要)的 implementations 和 configuration 位置。这反过来使得单独测试每个 persistence-related code 更容易。

  • Common 数据访问 exceptions。 Spring 可以从 ORM 工具中包装 exceptions,将它们从专有(可能已检查)的 exceptions 转换为 common 运行时 DataAccessException 层次结构。这个 feature 允许你只处理适当的层,处理大多数持久性 exceptions,它们是 non-recoverable,而不会烦人的样板捕获,抛出和 exception 声明。您仍然可以根据需要捕获和处理 exceptions。请记住,JDBC exceptions(包括 DB-specific 方言)也会转换为相同的层次结构,这意味着您可以在一致的编程 model 中使用 JDBC 执行某些操作。

  • 一般资源管理。 Spring application 上下文可以处理 Hibernate SessionFactory实例,JPA EntityManagerFactory实例,JDBC DataSource实例和其他相关资源的位置和 configuration。这使得这些值易于管理和更改。 Spring 提供高效,简单,安全的持久性资源处理。对于 example,使用 Hibernate 的相关 code 通常需要使用相同的 Hibernate Session来确保效率和正确的 transaction 处理。 Spring 通过 Hibernate SessionFactory公开当前Session,可以很容易地透明地创建和绑定Session到当前线程。因此,对于任何本地或 JTA transaction 环境,Spring 解决了典型 Hibernate 使用的许多慢性问题。

  • 集成的 transaction management。您可以通过@Transactional annotation 或通过在 XML configuration 文件中显式配置 transaction AOP 建议,使用声明性的 aspect-oriented 编程(AOP)样式方法拦截器包装 ORM code。在这两种情况下,都会为您处理 transaction 语义和 exception 处理(回滚等)。如下所述,在资源和 transaction management中,您还可以交换各种 transaction managers,而不会影响 ORM-related code。例如,您可以在本地 transactions 和 JTA 之间切换,在两种方案中都可以使用相同的完整服务(例如声明性 transactions)。此外,JDBC-related code 可以与您用于执行 ORM 的 code 进行事务性完全集成。这对于不适合 ORM 的数据访问很有用,例如批处理和 BLOB 流,它仍然需要与 ORM 操作共享 common transactions。

要获得更全面的 ORM 支持,包括对 MongoDB 等替代数据库技术的支持,您可能需要查看Spring Data项目套件。如果您是 JPA 用户,https://spring.io入门使用 JPA 访问数据指南提供了很好的介绍。

20.2 一般 ORM integration 注意事项

本节重点介绍适用于所有 ORM 技术的注意事项。 第 20.3 节,“ Hibernate”部分提供了更多详细信息,并在具体的 context 中显示了这些 features 和配置。

Spring 的 ORM integration 的主要目标是清晰的 application 分层,具有任何数据访问和 transaction 技术,以及 application objects 的松散耦合。没有更多的业务服务依赖于数据访问或 transaction 策略,没有更多的 hard-coded 资源查找,没有更多的 hard-to-replace 单例,没有更多的自定义服务注册表。一种简单而一致的方法,用于连接 application objects,使它们可以重复使用,并尽可能不受容器依赖。所有单独的数据访问 features 都可以单独使用,但可以很好地与 Spring 的 application context 概念集成,提供 XML-based configuration 和 cross-referencing 无需 Spring-aware 的普通 JavaBean 实例。在典型的 Spring application 中,许多重要的 objects 是 JavaBeans:数据访问模板,数据访问 objects,transaction managers,使用数据访问的业务服务 objects 和 transaction managers,web 视图解析器,web 控制器使用业务服务,等等。

20.2.1 资源和 transaction management

典型的业务应用程序混杂着重复的资源 management code。许多项目试图发明自己的解决方案,有时为了方便编程而牺牲正确的故障处理。 Spring 提出了适当资源处理的简单解决方案,即在 JDBC 的情况下通过模板化 IoC 并为 ORM 技术应用 AOP 拦截器。

基础结构提供适当的资源处理以及将特定 API exceptions 适当转换为未经检查的基础结构 exception 层次结构。 Spring 引入了 DAO exception 层次结构,适用于任何数据访问策略。对于直接 JDBC,上一节中提到的JdbcTemplate class 提供了连接处理和SQLExceptionDataAccessException层次结构的正确转换,包括将 database-specific SQL 错误代码转换为有意义的 exception classes。对于 ORM 技术,请参阅下一节,了解如何获得相同的 exception 转换优势。

当涉及 transaction management 时,JdbcTemplate class 挂钩到 Spring transaction 支持,并通过各自的 Spring transaction managers 支持 JTA 和 JDBC transactions。对于支持的 ORM 技术,Spring 通过 Hibernate,JPA 和 JDO transaction managers 以及 JTA 支持提供 Hibernate,JPA 和 JDO 支持。有关 transaction 支持的详细信息,请参阅第 17 章,交易管理章节。

20.2.2 Exception 翻译

在 DAO 中使用 Hibernate,JPA 或 JDO 时,必须决定如何处理持久性技术的本机 exception classes。根据技术,DAO 会抛出HibernateExceptionPersistenceExceptionJDOException的子类。这些 exceptions 都是 run-time exceptions,不必声明或捕获。您可能还需要处理IllegalArgumentExceptionIllegalStateException。这意味着调用者只能将 exceptions 视为通常致命的,除非他们想要依赖持久性技术自己的 exception 结构。如果不将调用者与 implementation 策略联系起来,则无法捕获诸如乐观锁定失败之类的特定原因。对于那些强烈不需要任何特殊的 exception 处理的应用程序来说,这种权衡可能是可以接受的。但是,Spring 允许通过@Repository annotation 透明地应用 exception 转换:

@Repository
public class ProductDaoImpl implements ProductDao {

    // class body here...

}
<beans>

    <!-- Exception translation bean post processor -->
    <bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"/>

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

</beans>

后处理器自动查找所有 exception 转换器(PersistenceExceptionTranslator接口的 implementations),并建议所有标记有@Repository annotation 的 beans,以便发现的转换器可以拦截并在抛出的 exceptions 上应用适当的转换。

总结:您可以基于普通持久性技术的 API 和 annotations 实现 DAO,同时仍然可以从 Spring 的自定义 exception 层次结构中受益于 Spring-managed transactions,依赖注入和透明 exception 转换(如果需要)。

20.3 Hibernate

我们将从 Spring 环境中的Hibernate 5开始,使用它来演示 Spring 在集成 O/R 映射器时采用的方法。本节将详细介绍许多问题,并显示 DAO implementations 和 transaction demarcation 的不同变体。大多数这些模式可以直接转换为所有其他支持的 ORM 工具。然后,本章的以下部分将介绍其他 ORM 技术,并在那里展示更简洁的示例。

从 Spring 4.0 开始,Spring 需要 Hibernate 3.6 或更高版本。请注意,Hibernate 团队在 3 年前停止支持 Hibernate,甚至在 2015 年底逐步取消对 Hibernate 4.x 的支持。因此,我们建议从 2016 年开始 Hibernate 5.0 及更高版本。

在 Spring 容器中设置 20.3.1 SessionFactory

为避免将 application objects 绑定到 hard-coded 资源查找,您可以在 Spring 容器中将资源(如 JDBC DataSource或 Hibernate SessionFactory)定义为 beans。需要访问资源的 Application objects 通过 bean references 接收 references 到这些预定义实例,如下一节中的 DAO 定义所示。

以下摘自 XML application context 定义显示了如何在其上设置 JDBC DataSource和 Hibernate SessionFactory

<beans>

    <bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <property name="driverClassName" value="org.hsqldb.jdbcDriver"/>
        <property name="url" value="jdbc:hsqldb:hsql://localhost:9001"/>
        <property name="username" value="sa"/>
        <property name="password" value=""/>
    </bean>

    <bean id="mySessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
        <property name="dataSource" ref="myDataSource"/>
        <property name="mappingResources">
            <list>
                <value>product.hbm.xml</value>
            </list>
        </property>
        <property name="hibernateProperties">
            <value>
                hibernate.dialect=org.hibernate.dialect.HSQLDialect
            </value>
        </property>
    </bean>

</beans>

从本地 Jakarta Commons _DBCP BasicDataSource切换到 JNDI-located DataSource(通常由 application 服务器管理)只需 configuration:

<beans>
    <jee:jndi-lookup id="myDataSource" jndi-name="java:comp/env/jdbc/myds"/>
</beans>

您还可以使用 Spring 的JndiObjectFactoryBean/<jee:jndi-lookup>来访问 JNDI-located SessionFactory来检索并公开它。但是,这通常不是 EJB context 之外的 common。

20.3.2 基于普通的 Hibernate API 实现 DAO

Hibernate 有一个称为上下文会话的 feature,其中 Hibernate 本身管理每个 transaction 的一个当前Session。这大致相当于 Spring 同步 Hibernate Session每 transaction。相应的 DAO implementation 类似于以下 example,基于 plain Hibernate API:

public class ProductDaoImpl implements ProductDao {

    private SessionFactory sessionFactory;

    public void setSessionFactory(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }

    public Collection loadProductsByCategory(String category) {
        return this.sessionFactory.getCurrentSession()
                .createQuery("from test.Product product where product.category=?")
                .setParameter(0, category)
                .list();
    }
}

除了将SessionFactory保存在实例变量中之外,此样式类似于 Hibernate reference 文档和示例的样式。我们强烈建议在 Hibernate 的 CaveatEmptor sample application 中对 old-school static HibernateUtil class 进行这样的 instance-based 设置。 (一般情况下,除非绝对 necessary.),否则不要在static变量中保留任何资源

上面的 DAO 遵循依赖注入 pattern:它非常适合 Spring IoC 容器,就像它对 Spring 的HibernateTemplate进行编码一样。当然,这样的 DAO 也可以用普通 Java 设置(例如,在单元测试中)。只需实例化它并使用所需的工厂参考调用setSessionFactory(..)。作为 Spring bean 定义,DAO 将类似于以下内容:

<beans>

    <bean id="myProductDao" class="product.ProductDaoImpl">
        <property name="sessionFactory" ref="mySessionFactory"/>
    </bean>

</beans>

这种 DAO 风格的主要优点是它仅依赖于 Hibernate API;不需要任何 Spring class 的 import。从 non-invasiveness 的角度来看,这当然很有吸引力,毫无疑问 Hibernate 开发人员会更自然。

但是,DAO 抛出普通HibernateException(未经检查,因此不必声明或捕获),这意味着调用者只能将 exceptions 视为致命 - 除非他们想要依赖 Hibernate 自己的 exception 层次结构。如果不将调用者与 implementation 策略联系起来,则无法捕获诸如乐观锁定失败之类的特定原因。对于那些强烈 Hibernate-based and/or 不需要任何特殊的 exception 处理的应用程序来说,这种权衡可能是可以接受的。

幸运的是,Spring 的LocalSessionFactoryBean支持 Hibernate 的SessionFactory.getCurrentSession()方法用于任何 Spring transaction 策略,即使用HibernateTransactionManager也返回当前的 Spring-managed transactional Session。当然,该方法的标准行为仍然是与正在进行的 JTA transaction 相关的当前Session的返回,如果有的话。无论您使用的是 Spring 的JtaTransactionManager,EJB 容器托管 transactions(CMT)还是 JTA,此行为都适用。

总结:您可以基于普通的 Hibernate API 实现 DAO,同时仍然可以参与 Spring-managed transactions。

20.3.3 声明性 transaction 划界

我们建议您使用 Spring 的声明性 transaction 支持,这使您可以使用 AOP transaction 拦截器替换 Java code 中的显式 transaction 分界 API calls。可以使用 Java annotations 或 XML 在 Spring 容器中配置此 transaction 拦截器。这种声明式 transaction 功能允许您保持业务服务不受重复的 transaction demarcation code 的影响,并专注于添加业务逻辑,这是您的 application 的真正值。

在继续之前,如果您还没有这样做,强烈建议您阅读第 17.5 节,“陈述性交易管理”

您可以使用@Transactional annotations 注释服务层,并指示 Spring 容器查找这些注释并为这些带注释的方法提供 transactional 语义。

public class ProductServiceImpl implements ProductService {

    private ProductDao productDao;

    public void setProductDao(ProductDao productDao) {
        this.productDao = productDao;
    }

    @Transactional
    public void increasePriceOfAllProductsInCategory(final String category) {
        List productsToChange = this.productDao.loadProductsByCategory(category);
        // ...
    }

    @Transactional(readOnly = true)
    public List<Product> findAllProducts() {
        return this.productDao.findAllProducts();
    }

}

您需要在容器中设置的所有内容都是PlatformTransactionManager implementation 作为 bean 以及“< hh:// +81+ .h >”条目,在运行时选择@Transactional处理。

<?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.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!-- SessionFactory, DataSource, etc. omitted -->

    <bean id="transactionManager"
            class="org.springframework.orm.hibernate5.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory"/>
    </bean>

    <tx:annotation-driven/>

    <bean id="myProductService" class="product.SimpleProductService">
        <property name="productDao" ref="myProductDao"/>
    </bean>

</beans>

20.3.4 程序化交叉划分

您可以在跨越任意数量操作的此类 lower-level 数据访问服务之上,在 application 的更高 level 中划分 transactions。对周围商业服务的实施也没有限制;它只需要一个 Spring PlatformTransactionManager。同样,后者可以来自任何地方,但最好是通过setTransactionManager(..)方法作为 bean reference,就像productDAO应该由setProductDao(..)方法设置一样。以下代码段在 Spring application context 中显示 transaction manager 和业务服务定义,并在业务方法 implementation 中显示 example:

<beans>

    <bean id="myTxManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager">
        <property name="sessionFactory" ref="mySessionFactory"/>
    </bean>

    <bean id="myProductService" class="product.ProductServiceImpl">
        <property name="transactionManager" ref="myTxManager"/>
        <property name="productDao" ref="myProductDao"/>
    </bean>

</beans>
public class ProductServiceImpl implements ProductService {

    private TransactionTemplate transactionTemplate;
    private ProductDao productDao;

    public void setTransactionManager(PlatformTransactionManager transactionManager) {
        this.transactionTemplate = new TransactionTemplate(transactionManager);
    }

    public void setProductDao(ProductDao productDao) {
        this.productDao = productDao;
    }

    public void increasePriceOfAllProductsInCategory(final String category) {
        this.transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            public void doInTransactionWithoutResult(TransactionStatus status) {
                List productsToChange = this.productDao.loadProductsByCategory(category);
                // do the price increase...
            }
        });
    }
}

Spring 的TransactionInterceptor允许使用回调 code 抛出任何已检查的 application exception,而TransactionTemplate仅限于回调中未经检查的 exceptions。 TransactionTemplate在未经检查的 application exception 的情况下触发回滚,或者 transaction 被 application 标记 rollback-only(通过TransactionStatus)。 TransactionInterceptor默认情况下的行为方式相同,但允许每种方法配置回滚 policies。

20.3.5 交易管理策略

TransactionTemplateTransactionInterceptor都将实际 transaction 处理委托给PlatformTransactionManager实例,该实例可以是HibernateTransactionManager(对于单个 Hibernate SessionFactory,在引擎盖下使用ThreadLocal Session)或JtaTransactionManager(委托容器的 JTA 子系统)为 Hibernate applications 。您甚至可以使用自定义PlatformTransactionManager implementation。从本机 Hibernate transaction management 切换到 JTA,例如在面向 application 的某些部署时遇到分布式 transaction 要求时,只需要 configuration。只需用 Spring 的 JTA transaction implementation 替换 Hibernate transaction manager。 transaction 划分和数据访问 code 都可以无需更改,因为它们只使用通用的 transaction management API。

对于跨多个 Hibernate session 工厂的分布式 transactions,只需将JtaTransactionManager作为 transaction 策略与多个LocalSessionFactoryBean定义相结合。然后每个 DAO 将一个特定的SessionFactory reference 传递到其对应的 bean property。如果所有基础 JDBC 数据源都是 transactional 容器,则业务服务可以跨越任意数量的 DAO 和任意数量的 session 工厂划分 transactions,而无需特别考虑,因为它使用JtaTransactionManager作为策略。

HibernateTransactionManagerJtaTransactionManager都允许使用 Hibernate 正确 JVM-level 缓存处理,没有 container-specific transaction manager 查找或 JCA 连接器(如果你没有使用 EJB 来启动 transactions)。

对于特定的DataSourceHibernateTransactionManager可以_portport Hibernate JDBC Connection到普通 JDBC 访问 code。如果只访问一个数据库,则此功能允许在没有 JTA 的情况下完全混合 Hibernate 和 JDBC 数据访问进行 high-level transaction 划分。如果通过LocalSessionFactoryBean class 的dataSource property 设置 passed-in SessionFactoryDataSourceHibernateTransactionManager会自动将 Hibernate transaction 公开为 JDBC transaction。或者,您可以通过HibernateTransactionManager class 的dataSource property 明确指定 transactions 应该公开的DataSource

20.3.6 比较 container-managed 和本地定义的资源

您可以在 container-managed JNDI SessionFactory和本地定义的之间切换,而无需更改 application code 的单个 line。是将资源定义保留在容器中还是本地保存在 application 中主要是您使用的 transaction 策略的问题。与 Spring-defined local SessionFactory相比,手动注册的 JNDI SessionFactory不提供任何好处。部署SessionFactory到 Hibernate 的 JCA 连接器提供了参与 Java EE 服务器的 management 基础结构的附加 value,但不会添加超出该值的实际 value。

Spring 的 transaction 支持不绑定到容器。使用除 JTA 之外的任何策略配置,transaction 支持也适用于 stand-alone 或测试环境。特别是在 single-database transactions 的典型情况下,Spring 的 single-resource local transaction 支持是 JTA 的轻量级和强大的替代品。当您使用本地 EJB stateless session beans 来驱动 transactions 时,您既依赖于 EJB 容器又依赖于 JTA,即使您只访问单个数据库,也只使用 stateless session beans 通过 container-managed transactions 提供声明性 transactions。此外,以编程方式直接使用 JTA 也需要 Java EE 环境。 JTA 不仅涉及 JTA 本身和 JNDI DataSource实例的容器依赖性。对于 non-Spring,JTA-driven Hibernate transactions,您必须使用 Hibernate JCA 连接器或额外的 Hibernate transaction code 并配置TransactionManagerLookup以进行正确的 JVM-level 缓存。

Spring-driven transactions 可以与本地定义的 Hibernate SessionFactory一样工作,就像它们访问单个数据库时本地 JDBC DataSource一样。因此,当您具有分布式 transaction 要求时,您只需使用 Spring 的 JTA transaction 策略。 JCA 连接器需要 container-specific 部署步骤,显然 JCA 支持。与使用本地资源定义和 Spring-driven transactions 部署简单的 web application 相比,此 configuration 需要更多的工作。此外,如果您使用的话,通常需要容器的企业版,例如,WebLogic Express,它不提供 JCA。具有本地资源的 Spring application 和跨越单个数据库的 transactions 可以在任何 Java EE web 容器(没有 JTA,JCA 或 EJB)中工作,例如 Tomcat,Resin,甚至是普通的 Jetty。此外,您可以轻松地在桌面应用程序或测试套件中重用这样的中间层。

考虑到所有事情,如果你不使用 EJB,坚持使用本地SessionFactory设置和 Spring 的HibernateTransactionManagerJtaTransactionManager。您可以获得所有好处,包括正确的 transactional JVM-level 缓存和分布式 transactions,而不会给容器部署带来不便。通过 JCA 连接器 JNDI 注册 Hibernate SessionFactory只在与 EJB 结合使用时才添加 value。

20.3.7 使用 Hibernate 进行虚假 application 服务器警告

在一些具有非常严格的XADataSource __mplement 的 JTA 环境中 - 当前只有一些 WebLogic Server 和 WebSphere 版本 - 当 Hibernate 配置而不考虑该环境的 JTA PlatformTransactionManager object 时,可能会在 application server log 中显示虚假警告或 exceptions。 。这些警告或 exceptions 指示正在访问的连接不再有效,或者 JDBC 访问不再有效,可能是因为 transaction 不再是 active。作为示例,这是 WebLogic 的实际 exception:

java.sql.SQLException: The transaction is no longer active - status: 'Committed'. No
further JDBC access is allowed within this transaction.

您可以通过简单地让 Hibernate 知道它将同步的 JTA PlatformTransactionManager实例(以及 Spring)来解决此警告。您有两种选择:

  • 如果在你的 application context 中你已经直接获得了 JTA PlatformTransactionManager object(可能是从 JNDI 到JndiObjectFactoryBean<jee:jndi-lookup>)并将其提供给_Spample 的JtaTransactionManager,那么最简单的方法就是指定一个 bean 来定义这个 JTA 实例作为LocalSessionFactoryBean. property @

  • 你很可能还没有 JTA PlatformTransactionManager实例,因为 Spring 的JtaTransactionManager可以自己找到它。因此,您需要配置 Hibernate 以直接查找 JTA PlatformTransactionManager。您可以通过在 Hibernate configuration 中配置 application server 特定的TransactionManagerLookup class 来完成此操作,如 Hibernate 手册中所述。

本节的其余部分描述了在 Hibernate 意识到 JTA PlatformTransactionManager的情况下发生的 events 的顺序。

当 Hibernate 没有配置任何 JTA PlatformTransactionManager的意识时,JTA transaction 提交时会发生以下 events:

  • JTA transaction 提交。

  • Spring 的JtaTransactionManager与 JTA transaction 同步,因此通过 JTA transaction manager 的 afterCompletion 回调调用它。

  • 在其他活动中,这种同步可以通过 Spring 到 Hibernate 触发回调,通过 Hibernate 的afterTransactionCompletion回调(用于清除 Hibernate 缓存),然后在 Hibernate Session 上进行显式close()调用,这会导致 Hibernate 尝试close() JDBC 连接。

  • 在某些环境中,此Connection.close()调用会触发警告或错误,因为 application 服务器根本不再认为Connection可用,因为 transaction 已经提交。

当 Hibernate 配置了 JTA PlatformTransactionManager的意识时,JTA transaction 提交时会发生以下 events:

  • JTA transaction 已准备好提交。

  • Spring 的JtaTransactionManager与 JTA transaction 同步,因此 transaction 通过 JTA transaction manager 的 beforeCompletion 回调来回调。

  • Spring 意识到 Hibernate 本身与 JTA transaction 同步,并且行为与前一个场景不同。假设 Hibernate Session需要完全关闭,Spring 现在将关闭它。

  • JTA transaction 提交。

  • Hibernate 与 JTA transaction 同步,因此 transaction 通过 JTA transaction manager 的 afterCompletion 回调调用,并且可以正确清除其缓存。

20.4 JDO

Spring 支持标准的 JDO 2.0 和 2.1 API 作为数据访问策略,遵循与 Hibernate 支持相同的样式。相应的 integration classes 驻留在org.springframework.orm.jdo包中。

20.4.1 PersistenceManagerFactory 设置

Spring 提供了LocalPersistenceManagerFactoryBean class,允许您在 Spring application context 中定义本地 JDO PersistenceManagerFactory

<beans>

    <bean id="myPmf" class="org.springframework.orm.jdo.LocalPersistenceManagerFactoryBean">
        <property name="configLocation" value="classpath:kodo.properties"/>
    </bean>

</beans>

或者,您可以通过直接实例化PersistenceManagerFactory implementation class 来设置PersistenceManagerFactory。 JDO PersistenceManagerFactory implementation class 跟在 JavaBeans pattern 之后,就像 JDBC DataSource implementation class 一样,它非常适合使用 Spring 的 configuration。此设置样式通常支持 Spring-defined JDBC DataSource,传递到connectionFactory property。例如,对于开源 JDO implementation DataNucleus(以前称为 JPOX)(http://www.datanucleus.org/),这是PersistenceManagerFactory implementation 的 XML configuration:

<beans>

 <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    <property name="driverClassName" value="${jdbc.driverClassName}"/>
    <property name="url" value="${jdbc.url}"/>
    <property name="username" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>
 </bean>

 <bean id="myPmf" class="org.datanucleus.jdo.JDOPersistenceManagerFactory" destroy-method="close">
    <property name="connectionFactory" ref="dataSource"/>
    <property name="nontransactionalRead" value="true"/>
 </bean>

</beans>

您还可以在 Java EE application 服务器的 JNDI 环境中设置 JDO PersistenceManagerFactory,通常通过特定 JDO implementation 提供的 JCA 连接器。 Spring 的标准JndiObjectFactoryBean<jee:jndi-lookup>可用于检索和公开这样的PersistenceManagerFactory。但是,在 EJB context 之外,在 JNDI 中保存PersistenceManagerFactory并没有真正的好处:只选择这样的设置是有充分理由的。见第 20.3.6 节,“比较 container-managed 和本地定义的资源”进行讨论; arguments 也适用于 JDO。

20.4.2 基于普通的 JDO API 实现 DAO

通过使用注入的PersistenceManagerFactory,DAO 也可以直接针对普通的 JDO API 编写,没有任何 Spring 依赖项。以下是相应 DAO implementation 的示例:

public class ProductDaoImpl implements ProductDao {

    private PersistenceManagerFactory persistenceManagerFactory;

    public void setPersistenceManagerFactory(PersistenceManagerFactory pmf) {
        this.persistenceManagerFactory = pmf;
    }

    public Collection loadProductsByCategory(String category) {
        PersistenceManager pm = this.persistenceManagerFactory.getPersistenceManager();
        try {
            Query query = pm.newQuery(Product.class, "category = pCategory");
            query.declareParameters("String pCategory");
            return query.execute(category);
        }
        finally {
            pm.close();
        }
    }
}
<beans>

    <bean id="myProductDao" class="product.ProductDaoImpl">
        <property name="persistenceManagerFactory" ref="myPmf"/>
    </bean>

</beans>

这样的 DAO 的主要问题是它们总是从工厂获得新的PersistenceManager。要访问 Spring-managed transactional PersistenceManager,请在目标PersistenceManagerFactory前面定义TransactionAwarePersistenceManagerFactoryProxy(包含在 Spring 中),然后将 reference 传递给您的 DAO,如下例所示:

<beans>

    <bean id="myPmfProxy"
            class="org.springframework.orm.jdo.TransactionAwarePersistenceManagerFactoryProxy">
        <property name="targetPersistenceManagerFactory" ref="myPmf"/>
    </bean>

    <bean id="myProductDao" class="product.ProductDaoImpl">
        <property name="persistenceManagerFactory" ref="myPmfProxy"/>
    </bean>

</beans>

您的数据访问 code 将从它 calls 的PersistenceManagerFactory.getPersistenceManager()方法接收 transactional PersistenceManager(如果有)。后一种方法调用通过代理进行,代理在从工厂获取新进程之前首先检查当前 transactional PersistenceManager。在 transactional PersistenceManager的情况下,PersistenceManager上的任何close() calls 都会被忽略。

如果您的数据访问 code 始终在 active transaction(或至少在 active transaction 同步中)内运行,则可以安全地省略PersistenceManager.close()调用,从而省略整个finally块,您可以这样做以保持 DAO implementations 简洁:

public class ProductDaoImpl implements ProductDao {

    private PersistenceManagerFactory persistenceManagerFactory;

    public void setPersistenceManagerFactory(PersistenceManagerFactory pmf) {
        this.persistenceManagerFactory = pmf;
    }

    public Collection loadProductsByCategory(String category) {
        PersistenceManager pm = this.persistenceManagerFactory.getPersistenceManager();
        Query query = pm.newQuery(Product.class, "category = pCategory");
        query.declareParameters("String pCategory");
        return query.execute(category);
    }
}

对于依赖 active transactions 的 DAO,建议您通过关闭TransactionAwarePersistenceManagerFactoryProxy's allowCreate` flag 来强制执行 active transactions:

<beans>

    <bean id="myPmfProxy"
            class="org.springframework.orm.jdo.TransactionAwarePersistenceManagerFactoryProxy">
        <property name="targetPersistenceManagerFactory" ref="myPmf"/>
        <property name="allowCreate" value="false"/>
    </bean>

    <bean id="myProductDao" class="product.ProductDaoImpl">
        <property name="persistenceManagerFactory" ref="myPmfProxy"/>
    </bean>

</beans>

这种 DAO 风格的主要优点是它仅依赖于 JDO API;不需要任何 Spring class 的 import。从 non-invasiveness 的角度来看,这当然很有吸引力,对于 JDO 开发人员来说可能会更自然。

但是,DAO 抛出普通JDOException(未经检查,因此不必声明或捕获),这意味着调用者只能将 exceptions 视为致命,除非您想依赖于 JDO 自己的 exception 结构。如果不将调用者与 implementation 策略联系起来,则无法捕获诸如乐观锁定失败之类的特定原因。对于那些强烈 JDO-based and/or 不需要任何特殊的 exception 处理的应用程序来说,这种权衡可能是可以接受的。

总之,您可以基于普通的 JDO API 进行 DAO,他们仍然可以参与 Spring-managed transactions。如果您已熟悉 JDO,此策略可能会吸引您。但是,这样的 DAO 抛出简单的JDOException,你必须显式转换为 Spring 的DataAccessException(如果需要)。

20.4.3 Transaction management

如果您还没有这样做,我们强烈建议您阅读第 17.5 节,“陈述性交易管理”,以便更详细地了解 Spring 的声明性 transaction 支持。

要在 transactions 中执行服务操作,可以使用 Spring 的 common 声明式 transaction 工具。例如:

<?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.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">

    <bean id="myTxManager" class="org.springframework.orm.jdo.JdoTransactionManager">
        <property name="persistenceManagerFactory" ref="myPmf"/>
    </bean>

    <bean id="myProductService" class="product.ProductServiceImpl">
        <property name="productDao" ref="myProductDao"/>
    </bean>

    <tx:advice id="txAdvice" transaction-manager="txManager">
        <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>

    <aop:config>
        <aop:pointcut id="productServiceMethods"
                expression="execution(* product.ProductService.*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="productServiceMethods"/>
    </aop:config>

</beans>

JDO 需要 active transaction 来修改持久性 object。与 Hibernate 相比,JDO 中不存在 non-transactional flush 概念。因此,您需要为特定环境设置所选的 JDO implementation。具体来说,您需要为 JTA 同步明确设置它,以检测 active JTA transaction 本身。对于由 Spring 的JdoTransactionManager执行的本地 transactions,这不是必需的,但是有必要参与 JTA transactions,无论是由 Spring 的JtaTransactionManager还是由 EJB CMT 和普通的 JTA 驱动。

JdoTransactionManager能够将 JDO transaction 暴露给访问相同 JDBC DataSource的 JDBC 访问 code,前提是已注册的JdoDialect支持检索底层 JDBC Connection。默认情况下,JDBC-based JDO 2.0 implementations 就是这种情况。

20.4.4 JdoDialect

作为高级 feature,LocalPersistenceManagerFactoryBeanJdoTransactionManager都支持可以传递到jdoDialect bean property 的自定义JdoDialect。使用JdoDialect implementation,您可以启用 Spring 支持的高级 features,通常以 vendor-specific 方式:

  • 应用特定的 transaction 语义,例如自定义隔离 level 或 transaction timeout

  • 检索 transactional JDBC Connection以暴露给 JDBC-based DAO

  • 应用查询超时,这些超时是从 Spring-managed transaction 超时自动计算

  • 急切地刷新PersistenceManager,使 transactional 变化对 JDBC-based 数据访问 code 可见

  • JDOExceptions到 Spring DataAccessExceptions的高级翻译

有关其操作以及如何在 Spring 的 JDO 支持中使用它们的更多详细信息,请参阅JdoDialect javadocs。

20.5 JPA

org.springframework.orm.jpa包下可用的 Spring JPA 以与 Hibernate 或 JDO 的 integration 类似的方式为Java Persistence API提供全面支持,同时了解 order 中的基础 implementation 以提供额外的 features。

20.5.1 在 Spring 环境中进行 JPA 设置的三个选项

Spring JPA 支持提供了三种设置 JPA EntityManagerFactory的方法,application 将使用它来获取实体 manager。

LocalEntityManagerFactoryBean

仅在简单部署环境(例如 stand-alone applications 和 integration 测试)中使用此选项。

LocalEntityManagerFactoryBean创建EntityManagerFactory适用于简单的部署环境,其中 application 仅使用 JPA 进行数据访问。工厂 bean 使用 JPA PersistenceProvider自动检测机制(根据 JPA 的 Java SE 引导),并且在大多数情况下,要求您仅指定持久性单元 name:

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

这种形式的 JPA 部署是最简单和最有限的。您不能引用现有的 JDBC DataSource bean 定义,也不存在对 global transactions 的支持。此外,持久 class 的编织(byte-code 转换)是 provider-specific,通常需要在启动时指定特定的 JVM 代理。此选项仅适用于为其设计 JPA 规范的 stand-alone applications 和测试环境。

从 JNDI 获取 EntityManagerFactory

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

从 JNDI 获取EntityManagerFactory(对于 Java EE 环境中的 example),只需更改 XML configuration 即可:

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

此操作假定标准 Java EE 引导:Java EE 服务器自动检测持久性单元(实际上, applicationjars 中的META-INF/persistence.xml files)和 Java EE 部署描述符中的persistence-unit-ref条目(对于 example,web.xml),并为这些持久性单元定义环境命名 context 位置。

在这种情况下,整个持久性单元部署(包括持久 classes 的编织(byte-code 转换))由 Java EE 服务器决定。 JDBC DataSource是通过META-INF/persistence.xml文件中的 JNDI 位置定义的; EntityManager transactions 与服务器的 JTA 子系统集成在一起。 Spring 仅使用获得的EntityManagerFactory,通过依赖注入将其传递给 application objects,并通常通过JtaTransactionManager管理持久性单元的 transactions。

如果在同一个 application 中使用了多个持久性单元,那么持久性单元的 bean 名称应该_ 匹配 application 用于引用它们的持久性单元名称,例如,在@PersistenceUnit@PersistenceContext 注释中。

LocalContainerEntityManagerFactoryBean

在 Spring-based application 环境中使用此选项可获得完整的 JPA 功能。这包括 web 容器(如 Tomcat)以及具有复杂持久性要求的 stand-alone applications 和 integration 测试。

LocalContainerEntityManagerFactoryBean完全控制EntityManagerFactory configuration,适用于需要 fine-grained 自定义的环境。 LocalContainerEntityManagerFactoryBean基于persistence.xml文件,提供的dataSourceLookup策略和指定的loadTimeWeaver创建PersistenceUnitInfo实例。因此,可以使用 JNDI 之外的自定义数据源并控制编织 process。以下 example 显示了LocalContainerEntityManagerFactoryBean的典型 bean 定义:

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

以下 example 显示了一个典型的persistence.xml文件:

<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/>快捷方式表示不应扫描带注释的实体 classes。指定的显式'true'value - <exclude-unlisted-classes>true</exclude-unlisted-classes/> - 也表示不扫描。 <exclude-unlisted-classes>false</exclude-unlisted-classes/>会触发扫描;但是,如果要进行实体 class 扫描,建议简单地省略exclude-unlisted-classes元素。

使用LocalContainerEntityManagerFactoryBean是最强大的 JPA 设置选项,允许在 application 中灵活的本地配置。它支持指向现有 JDBC DataSource的链接,支持 local 和 global transactions 等。但是,它还对运行时环境施加了要求,例如,如果持久性提供程序需要 byte-code 转换,则 weaving-capable class 加载程序的可用性。

此选项可能与 Java EE 服务器的 built-in JPA 功能冲突。在完整的 Java EE 环境中,请考虑从 JNDI 获取EntityManagerFactory。或者,在LocalContainerEntityManagerFactoryBean定义上指定自定义persistenceXmlLocation,对于 example,META-INF/my-persistence.xml,并且只在 application jar files 中包含具有该 name 的描述符。由于 Java EE 服务器仅查找默认的META-INF/persistence.xml files,因此它会忽略此类自定义持久性单元,从而避免与之前的 Spring-driven JPA 设置冲突。 (这适用于 Resin 3.1,适用于 example.)


什么时候需要 load-time 编织?

并非所有 JPA 提供程序都需要 JVM 代理。 Hibernate 是一个没有的示例。如果您的提供商不需要代理或您有其他选择,例如通过自定义编译器或 ant 任务在 build time 应用增强功能,则不应使用 load-time weaver。


LoadTimeWeaver接口是一个 Spring-provided class,它允许以特定方式插入 JPA ClassTransformer实例,具体取决于环境是 web 容器还是 application 服务器。通过代理人挂钩ClassTransformers通常效率不高。代理程序可以对整个虚拟机进行操作,并检查每个加载的 class,这在 production 服务器环境中通常是不受欢迎的。

Spring 为各种环境提供了许多LoadTimeWeaver _implement,允许ClassTransformer实例仅应用于 class 加载器而不是每个 VM。

有关LoadTimeWeaver __mplement 及其设置的更多信息,请参阅 AOP 章节中的名为“Spring configuration”的部分,无论是通用的还是针对各种平台定制的(例如 Tomcat,WebLogic,GlassFish,Resin 和 JBoss)。

如前面部分所述,您可以使用context:load-time-weaver _注释context:load-time-weaver XML 元素来配置 context-wide LoadTimeWeaver。所有 JPA LocalContainerEntityManagerFactoryBeans都会自动拾取这样的 global weaver。这是设置 load-time weaver 的首选方法,提供平台的自动检测(WebLogic,GlassFish,Tomcat,Resin,JBoss 或 VM 代理)以及将 weaver 自动传播到所有 weaver-aware beans:

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

但是,如果需要,可以通过loadTimeWeaver property 手动指定专用编织器:

<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 implementations 时,因为 JPA 转换器仅应用于 class loader level,因此彼此隔离。

处理多个持久性单元

对于依赖于多个持久性单元位置的 applications,存储在 classpath 中的各种 JARS 中,对于 example,Spring 提供PersistenceUnitManager作为中央 repository 并避免持久性单元 discovery process,这可能很昂贵。默认的 implementation 允许指定多个位置,这些位置被解析并稍后通过持久性单元 name 检索。 (默认情况下,在 classpath 中搜索META-INF/persistence.xml files.)

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

默认_impleration 允许自定义PersistenceUnitInfo实例,然后通过其 properties 以声明方式提供给 JPA 提供程序,这些实例会影响所有托管单元,或者通过PersistenceUnitPostProcessor进行编程,这允许选择持久性单元。如果未指定PersistenceUnitManager,则会在LocalContainerEntityManagerFactoryBean内部创建并使用一个PersistenceUnitManager

20.5.2 基于 JPA 实现 DAO:EntityManagerFactory 和 EntityManager

虽然EntityManagerFactory实例是 thread-safe,但EntityManager实例不是。注入的 JPA EntityManager的行为类似于从 application 服务器的 JNDI 环境中获取的EntityManager,如 JPA 规范所定义。它将所有 calls 委托给当前 transactional EntityManager,如果有的话;否则,它会回退到每个操作新创建的EntityManager,实际上使其使用 thread-safe。

通过使用注入的EntityManagerFactoryEntityManager,可以在没有任何 Spring 依赖项的情况下对普通 JPA 编写 code。如果启用了PersistenceAnnotationBeanPostProcessor,Spring 可以在字段和方法 level 上理解@PersistenceUnit@PersistenceContext 注释。使用@PersistenceUnit annotation 的普通 JPA DAO implementation 可能如下所示:

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

上面的 DAO 不依赖于 Spring,并且仍然可以很好地适应 Spring application context。此外,DAO 利用 annotations 要求注入默认EntityManagerFactory

<beans>

    <!-- bean post-processor for JPA annotations -->
    <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"/>

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

</beans>

作为显式定义PersistenceAnnotationBeanPostProcessor的替代方法,请考虑在 application context configuration 中使用 Spring context:annotation-config XML 元素。这样做会自动将所有 Spring 标准 post-processors 注册为 annotation-based configuration,包括CommonAnnotationBeanPostProcessor等等。

<beans>

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

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

</beans>

这样一个 DAO 的主要问题是它总是通过工厂创建一个新的EntityManager。您可以通过请求 transactional EntityManager(也称为“共享 EntityManager”,因为它是实际 transactional EntityManager 的共享,thread-safe 代理)而不是工厂来避免这种情况:

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 annotation 有一个可选属性type,默认为PersistenceContextType.TRANSACTION。此默认值是接收共享 EntityManager 代理所需的内容。替代方案PersistenceContextType.EXTENDED是完全不同的事情:这导致 so-called 扩展的 EntityManager,它不是 thread-safe,因此不能在并发访问的 component 中使用,例如 Spring-managed singleton bean。扩展的 EntityManagers 仅应用于有状态组件,例如,它们驻留在 session 中,EntityManager 的生命周期不依赖于当前的 transaction,而是完全取决于 application。


方法 - 和 field-level 注射

表示依赖注入的注释(例如@PersistenceUnit@PersistenceContext)可以应用于 class 中的字段或方法,因此表达式 method-level 注入和 field-level 注入。 Field-level 注释简洁易用,而 method-level 允许进一步处理注入的依赖项。在这两种情况下,成员可见性(公共,受保护,私有)都无关紧要。

class-level 注释怎么样?

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


注入的EntityManager是 Spring-managed(意识到正在进行的 transaction)。需要注意的是,即使新的 DAO implementation 使用_le注入EntityManager而不是EntityManagerFactory,但由于 annotation 用法,application context XML 不需要进行任何更改。

这种 DAO 风格的主要优点是它只依赖于 Java Persistence API;不需要任何 Spring class 的 import。此外,当理解 JPA 注释时,Spring 容器会自动应用注入。从 non-invasiveness 的角度来看,这很有吸引力,对于 JPA 开发人员来说可能会更自然。

20.5.3 Spring-driven JPA transactions

如果您还没有这样做,我们强烈建议您阅读第 17.5 节,“陈述性交易管理”,以便更详细地了解 Spring 的声明性 transaction 支持。

JPA 推荐的策略是通过 JPA 的本地 transaction 支持本地 transactions。 Spring 的JpaTransactionManager提供了许多本地 JDBC transactions 已知的功能,例如 transaction-specific 隔离级别和 resource-level read-only 优化,针对任何常规 JDBC 连接池(无 XA 要求)。

Spring JPA 还允许配置的JpaTransactionManager将 JPA transaction 暴露给访问相同DataSource的 JDBC 访问 code,前提是已注册的JpaDialect支持检索底层 JDBC Connection。开箱即用,Spring 为 EclipseLink,Hibernate 和 OpenJPA JPA implementations 提供方言。有关JpaDialect机制的详细信息,请参阅下一节。

20.5.4 JpaDialect 和 JpaVendorAdapter

作为高级 feature JpaTransactionManagerAbstractEntityManagerFactoryBean的子类支持自定义JpaDialect,要传递给jpaDialect bean property。 JpaDialect implementation 可以启用 Spring 支持的一些高级 features,通常以 vendor-specific 方式:

  • 应用特定的 transaction 语义,例如自定义隔离 level 或 transaction timeout)

  • 检索 transactional JDBC Connection以暴露给 JDBC-based DAO)

  • PersistenceExceptions到 Spring DataAccessExceptions的高级翻译

这对于特殊的 transaction 语义和 exception 的高级转换特别有用。使用的默认 implementation(DefaultJpaDialect)不提供任何特殊功能,如果需要上述 features,则必须指定相应的方言。

作为一个更广泛的提供者适应设施,主要用于 Spring 的 full-featured LocalContainerEntityManagerFactoryBean设置,JpaVendorAdapterJpaDialect的功能与其他 provider-specific 默认值相结合。指定HibernateJpaVendorAdapterEclipseLinkJpaVendorAdapter分别是 Hibernate 或 EclipseLink 的 auto-configuring EntityManagerFactory设置的最便捷方式。请注意,这些提供程序适配器主要设计用于 Spring-driven transaction management,i.e。用于JpaTransactionManager

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

20.5.5 使用 JTA transaction management 设置 JPA

作为JpaTransactionManager的替代,Spring 还允许通过 JTA 进行 multi-resource transaction 协调,无论是在 Java EE 环境中还是与独立的 transaction 协调器(如 Atomikos)。除了选择 Spring 的JtaTransactionManager而不是JpaTransactionManager之外,还有一些其他步骤:

  • 底层 JDBC 连接池需要 XA-capable 并与 transaction 协调器集成。这在 Java EE 环境中通常很简单,只需通过 JNDI 公开不同类型的DataSource。检查您的 application 服务器文档以获取详细信息。类似地,一个独立的 transaction 协调器通常带有特殊的 XA-integrated DataSource __mplement;再次检查它的 docs。

  • 需要为 JTA 配置 JPA EntityManagerFactory设置。这是 provider-specific,通常通过特殊的 properties 在LocalContainerEntityManagerFactoryBean上指定为“jpaProperties”。在 Hibernate 的情况下,这些 properties 甚至是 version-specific;请查看您的 Hibernate 文档以获取详细信息。

  • Spring 的HibernateJpaVendorAdapter强制执行某些 Spring-oriented 默认值,例如连接释放模式“on-close”,它与 Hibernate 在 Hibernate 5.0 中的默认值相匹配,但在 5.1/5.2 中不再存在。对于 JTA 设置,要么不声明HibernateJpaVendorAdapter开始,要么关闭其prepareConnection flag。或者,将 Hibernate 5.2 的“hibernate.connection.handling_mode”属性设置为“DELAYED_ACQUISITION_AND_RELEASE_AFTERSTATEMENT”以恢复 Hibernate 自己的默认值。有关 WebLogic 的相关说明,请参见第 20.3.7 节,“带有 Hibernate 的虚假应用服务器警告”

  • 或者,考虑从 application 服务器本身 i.e 获取EntityManagerFactory。通过 JNDI 查找而不是本地声明的LocalContainerEntityManagerFactoryBean。 server-provided EntityManagerFactory可能需要在服务器 configuration 中使用特殊定义,这使得部署的可移植性降低,但是将为服务器的 JTA 环境设置开箱即用。

Updated at: 9 months ago
初始化依赖于数据库的其他组件Table of content21. 使用 O/X Mappers 编组 XML