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

20.1 ORM与Spring简介

Spring Framework支持与Hibernate,Java Persistence API(JPA)和Java Data Objects(JDO)集成,以实现资源管理,数据访问对象(DAO)实现和事务策略。例如,对于Hibernate,有一些一流的支持,具有几个方便的IoC功能,可以解决许多典型的Hibernate集成问题。您可以通过依赖注入为O / R(对象关系)映射工具配置所有支持的功能。他们可以参与Spring的资源和事务管理,并且遵守Spring的通用事务和DAO异常层次结构。推荐的集成方式是针对普通的Hibernate,JPA和JDO API编写DAO代码。不再推荐使用Spring的DAO模板的较旧风格;但是,这种风格的覆盖范围可以在附录的 Section 39.1, “Classic ORM usage” 中找到。

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

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

  • 更容易测试。 Spring的IoC方法可以轻松交换Hibernate SessionFactory 实例,JDBC DataSource 实例,事务管理器和映射对象实现(如果需要)的实现和配置位置。这反过来使得单独测试每个与持久性相关的代码变得更加容易。

  • 公共数据访问异常。 Spring可以从ORM工具中包装异常,将它们从专有(可能已检查)的异常转换为公共运行时DataAccessException层次结构。此功能允许您处理大多数不可恢复的持久性异常,仅在适当的层中处理,而不会产生令人讨厌的样板捕获,抛出和异常声明。您仍然可以根据需要捕获和处理异常。请记住,JDBC异常(包括特定于DB的方言)也会转换为相同的层次结构,这意味着您可以在一致的编程模型中使用JDBC执行某些操作。

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

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

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

20.2 一般ORM集成注意事项

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

Spring的ORM集成的主要目标是清晰的应用程序分层,任何数据访问和事务技术,以及应用程序对象的松散耦合。没有更多的业务服务依赖于数据访问或事务策略,没有更多的硬编码资源查找,没有更难以替换的单例,没有更多的自定义服务注册表。一种简单而一致的方法,用于连接应用程序对象,使它们可以重用,并且尽可能不受容器依赖。所有单独的数据访问功能都可以单独使用,但与Spring的应用程序上下文概念很好地集成,提供基于XML的配置和不需要Spring感知的普通JavaBean实例的交叉引用。在典型的Spring应用程序中,许多重要的对象是JavaBeans:数据访问模板,数据访问对象,事务管理器,使用数据访问对象和事务管理器的业务服务,Web视图解析器,使用业务服务的Web控制器等等。

20.2.1 资源和事务管理

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

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

在事务管理方面, JdbcTemplate 类通过各自的Spring事务管理器 hook Spring事务支持并支持JTA和JDBC事务。对于支持的ORM技术,Spring通过Hibernate,JPA和JDO事务管理器以及JTA支持提供Hibernate,JPA和JDO支持。有关事务支持的详细信息,请参阅 Chapter 17, Transaction Management 一章。

20.2.2 异常翻译

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

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

后处理器自动查找所有异常转换器( PersistenceExceptionTranslator 接口的实现)并建议所有标记有 @Repository 注释的bean,以便发现的转换器可以拦截并对引发的异常应用适当的转换。

总结:你可以基于普通持久性技术的API和注释实现DAO,同时仍然受益于Spring管理的事务,依赖注入和Spring的自定义异常层次结构的透明异常转换(如果需要)。

20.3 休眠

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

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

20.3.1 在Spring容器中设置SessionFactory

为避免将应用程序对象绑定到硬编码资源查找,可以在Spring容器中将资源(如JDBC DataSource 或Hibernate SessionFactory )定义为bean。需要访问资源的应用程序对象通过bean引用接收对此类预定义实例的引用,如下一节中的DAO定义所示。

以下摘自XML应用程序上下文定义显示了如何在其上设置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的 DataSource (通常由应用程序服务器管理)只需配置:

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

您还可以使用Spring的 JndiObjectFactoryBean / <jee:jndi-lookup> 来访问位于JNDI的 SessionFactory ,以检索并公开它。但是,这通常在EJB上下文之外并不常见。

20.3.2 基于普通的Hibernate API实现DAO

Hibernate有一个称为上下文会话的功能,其中Hibernate本身管理每个事务的一个当前 Session 。这大致相当于Spring每次事务同步一个Hibernate Session 。相应的DAO实现类似于以下示例,基于普通的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参考文档和示例的样式。我们强烈建议在Hibernate的CaveatEmptor示例应用程序的老式 static HibernateUtil 类上进行基于实例的设置。 (一般情况下,除非绝对必要,否则不要在 static 变量中保留任何资源。)

上面的DAO遵循依赖注入模式:它非常适合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类。从非侵入性的角度来看,这当然很有吸引力,毫无疑问Hibernate开发人员会更自然。

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

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

总结:您可以基于普通的Hibernate API实现DAO,同时仍然可以参与Spring管理的事务。

20.3.3 声明性事务划分

我们建议您使用Spring的声明式事务支持,它允许您使用AOP事务拦截器替换Java代码中的显式事务划分API调用。可以使用Java注释或XML在Spring容器中配置此事务拦截器。此声明式事务功能允许您保持业务服务不受重复性事务的影响划分代码并专注于添加业务逻辑,这是您的应用程序的真正 Value 。

在继续之前,强烈建议您阅读第17.5节“声明式事务管理”(如果您尚未这样做)。

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

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 实现作为bean以及 "tx:annotation-driven/" 条目,在运行时选择 @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 程序化 Transaction 划分

除了跨越任意数量的操作的这种较低级别的数据访问服务之外,您还可以在更高级别的应用程序中划分事务。对周边业务服务的实施也没有限制;它只需要一个Spring PlatformTransactionManager 。同样,后者可以来自任何地方,但最好通过 setTransactionManager(..) 方法作为bean引用,就像 productDAO 应该由 setProductDao(..) 方法设置一样。以下代码段显示了Spring应用程序上下文中的事务管理器和业务服务定义,以及业务方法实现的示例:

<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 允许使用回调代码抛出任何已检查的应用程序异常,而 TransactionTemplate 仅限于回调中未经检查的异常。 TransactionTemplate 在未经检查的应用程序异常的情况下触发回滚,或者如果事务被应用程序标记为仅回滚(通过 TransactionStatus )。 TransactionInterceptor 默认情况下的行为方式相同,但每个方法允许可配置的回滚策略。

20.3.5 Transaction 管理策略

TransactionTemplateTransactionInterceptor 都将实际的事务处理委托给 PlatformTransactionManager 实例,该实例可以是 HibernateTransactionManager (对于单个Hibernate SessionFactory ,在引擎盖下使用 ThreadLocal Session )或 JtaTransactionManager (委托给容器的JTA子系统)用于Hibernate应用程序。您甚至可以使用自定义 PlatformTransactionManager 实现。从本机Hibernate事务管理切换到JTA,例如在面对应用程序的某些部署的分布式事务要求时,只需要进行配置。只需用Spring的JTA事务实现替换Hibernate事务管理器。事务划分和数据访问代码都可以无变化地工作,因为它们只使用通用事务管理API。

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

HibernateTransactionManagerJtaTransactionManager 都允许使用Hibernate进行正确的JVM级缓存处理,无需特定于容器的事务管理器查找或JCA连接器(如果您不使用EJB来启动事务)。

HibernateTransactionManager 可以将Hibernate JDBC Connection 导出为普通的JDBC访问代码,用于特定的 DataSource 。如果您只访问一个数据库,则此功能允许完全没有JTA的混合Hibernate和JDBC数据访问的高级事务划分。如果您通过 LocalSessionFactoryBean 类的 dataSource 属性设置了带有 DataSource 的传入 SessionFactory ,则 HibernateTransactionManager 会自动将Hibernate事务公开为JDBC事务。或者,您可以通过 HibernateTransactionManager 类的 dataSource 属性显式指定应为其公开事务的 DataSource

20.3.6 比较容器管理的资源和本地定义的资源

您可以在容器管理的JNDI SessionFactory 和本地定义的JNDI之间切换,而无需更改单行应用程序代码。是将资源定义保留在容器中还是本地保存在应用程序中主要取决于您使用的事务策略。与Spring定义的本地 SessionFactory 相比,手动注册的JNDI SessionFactory 不提供任何好处。通过Hibernate的JCA连接器部署 SessionFactory 提供了参与Java EE服务器管理基础结构的附加 Value ,但不会增加实际值。

Spring的事务支持不限于容器。使用除JTA之外的任何策略进行配置,事务支持也可以在独立或测试环境中运行。特别是在单数据库事务的典型情况下,Spring的单资源本地事务支持是JTA的轻量级和强大的替代方案。当您使用本地EJB无状态时用于驱动事务的会话bean,即使您只访问单个数据库,也依赖于EJB容器和JTA,并且仅使用无状态会话bean通过容器管理的事务提供声明性事务。此外,以编程方式直接使用JTA也需要Java EE环境。 JTA不仅涉及JTA本身和JNDI DataSource 实例的容器依赖性。对于非Spring,JTA驱动的Hibernate事务,您必须使用Hibernate JCA连接器或额外的Hibernate事务代码,并将 TransactionManagerLookup 配置为正确的JVM级缓存。

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

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

20.3.7 使用Hibernate发出虚假应用程序服务器警告

在一些具有非常严格的 XADataSource 实现的JTA环境中 - 当前只有一些WebLogic Server和WebSphere版本 - 当配置Hibernate而不考虑该环境的JTA PlatformTransactionManager 对象时,可能会在应用程序服务器日志中显示虚假警告或异常。这些警告或异常表明正在访问的连接不再有效,或者JDBC访问不再有效,可能是因为事务不再处于活动状态。例如,以下是WebLogic的实际异常:

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

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

  • 如果在您的应用程序上下文中,您已经直接获取JTA PlatformTransactionManager 对象(可能是从JNDI到 JndiObjectFactoryBean<jee:jndi-lookup> )并将其提供给Spring的 JtaTransactionManager ,那么最简单的方法是指定对定义此JTA的bean的引用 PlatformTransactionManager 实例作为 LocalSessionFactoryBean. 属性的 jtaTransactionManager 属性的值然后使该对象可用于Hibernate。

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

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

当Hibernate未配置任何JTA PlatformTransactionManager 的意识时,JTA事务提交时会发生以下事件:

  • JTA事务提交。

  • Spring的 JtaTransactionManager 与JTA事务同步,因此JTA事务管理器通过afterCompletion回调调用它。

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

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

当Hibernate配置为了解JTA PlatformTransactionManager 时,JTA事务提交时会发生以下事件:

  • JTA事务已准备好提交。

  • Spring的 JtaTransactionManager 与JTA事务同步,因此JTA事务管理器通过beforeCompletion回调调用该事务。

  • Spring意识到Hibernate本身已经与JTA事务同步,并且表现得很好与上一个场景不同。假设Hibernate Session 需要完全关闭,Spring现在将关闭它。

  • JTA事务提交。

  • Hibernate与JTA事务同步,因此事务由JTA事务管理器通过afterCompletion回调回调,并且可以正确清除其缓存。

20.4 JDO

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

20.4.1 PersistenceManagerFactory设置

Spring提供了一个 LocalPersistenceManagerFactoryBean 类,允许您在Spring应用程序上下文中定义本地JDO PersistenceManagerFactory

<beans>

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

</beans>

或者,您可以通过直接实例化 PersistenceManagerFactory 实现类来设置 PersistenceManagerFactory 。 JDO PersistenceManagerFactory 实现类遵循JavaBeans模式,就像JDBC DataSource 实现类一样,它非常适合使用Spring的配置。此设置样式通常支持Spring-defined JDBC DataSource ,传递到 connectionFactory 属性。例如,对于开源JDO实现DataNucleus(以前称为JPOX)( http://www.datanucleus.org/ ),这是 PersistenceManagerFactory 实现的XML配置:

<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应用程序服务器的JNDI环境中设置JDO PersistenceManagerFactory ,通常通过特定JDO实现提供的JCA连接器。 Spring的标准 JndiObjectFactoryBean<jee:jndi-lookup> 可用于检索和公开这样的 PersistenceManagerFactory 。但是,在EJB上下文之外,在JNDI中保存 PersistenceManagerFactory 并没有真正的好处:只选择这样的设置是有充分理由的。见 Section 20.3.6, “Comparing container-managed and locally defined resources” 进行讨论;这些论点也适用于JDO。

20.4.2 基于普通的JDO API实现DAO

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

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管理的事务 PersistenceManager ,请在目标 PersistenceManagerFactory 前面定义 TransactionAwarePersistenceManagerFactoryProxy (包含在Spring中),然后将对该代理的引用传递到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>

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

如果您的数据访问代码始终在活动事务中运行(或者至少在活动事务同步中运行),则可以安全地省略 PersistenceManager.close() 调用,从而省略整个 finally 块,您可以这样做以保持DAO实现简洁:

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

对于依赖于活动事务的此类DAO,建议您通过关闭 TransactionAwarePersistenceManagerFactoryProxy’sallowCreate` 标志来强制执行活动事务:

<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类。从非侵入性的角度来看,这当然具有吸引力,并且可能对JDO开发人员来说更自然。

但是,DAO抛出了简单的 JDOException (未经检查,因此不必声明或捕获),这意味着调用者只能将异常视为致命异常,除非您想依赖于JDO自己的异常结构。如果不将调用者与实现策略联系起来,则无法捕获诸如乐观锁定失败之类的特定原因。对于基于JDO且/或不需要任何特殊异常处理的应用程序,这种权衡可能是可接受的。

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

20.4.3 Transaction 管理

强烈建议您阅读第17.5节“声明式事务管理”(如果您还没有这样做),以获得有关Spring声明式事务支持的更详细报道。

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

<?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需要活动事务来修改持久对象。与Hibernate相比,JDO中不存在非事务性刷新概念。因此,您需要为特定环境设置所选的JDO实现。具体来说,您需要为JTA同步显式设置它,以检测活动的JTA事务本身。这对于本地 Transaction 不是必需的由Spring的 JdoTransactionManager 执行,但是有必要参与JTA事务,无论是由Spring的 JtaTransactionManager 还是由EJB CMT和普通的JTA驱动。

JdoTransactionManager 能够将JDO事务暴露给访问相同JDBC DataSource 的JDBC访问代码,前提是已注册的 JdoDialect 支持检索基础JDBC Connection 。默认情况下,基于JDBC的JDO 2.0实现就是这种情况。

20.4.4 JdoDialect

作为一项高级功能, LocalPersistenceManagerFactoryBeanJdoTransactionManager 都支持可以传递到 jdoDialect bean属性的自定义 JdoDialect 。使用 JdoDialect 实现,您可以启用Spring支持的高级功能,通常以特定于供应商的方式:

  • 应用特定的事务语义,例如自定义隔离级别或事务超时

  • 检索事务JDBC Connection 以暴露给基于JDBC的DAO

  • 应用查询超时,这是从Spring管理的事务超时自动计算的

  • 急切地刷新 PersistenceManager, 以使基于JDBC的数据访问代码可以看到事务更改

  • JDOExceptions 到Spring的高级翻译 DataAccessExceptions

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

20.5 JPA

org.springframework.orm.jpa 包中提供的Spring JPA以与Hibernate或JDO集成类似的方式为_681261提供全面支持,同时了解底层实现以提供其他功能。

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

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

LocalEntityManagerFactoryBean

仅在简单部署环境(例如独立应用程序和集成测试)中使用此选项。

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

<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配置:

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

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

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

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

LocalContainerEntityManagerFactoryBean

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

LocalContainerEntityManagerFactoryBean 完全控制 EntityManagerFactory 配置,适用于需要细粒度自定义的环境。 LocalContainerEntityManagerFactoryBean 基于 persistence.xml 文件,提供的 dataSourceLookup 策略和指定的 loadTimeWeaver 创建 PersistenceUnitInfo 实例。因此,可以使用JNDI之外的自定义数据源并控制编织过程。以下示例显示了 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>

该以下示例显示了典型的 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>

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

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

此选项可能与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。)


When is load-time weaving required?

并非所有JPA提供程序都需要JVM代理。 Hibernate就是一个没有的例子。如果您的提供程序不需要代理或您有其他替代方法,例如通过自定义编译器或ant任务在构建时应用增强功能,则不应使用加载时编织器。


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

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

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

如前面部分所述,您可以使用 context:load-time-weaver 注释 context:load-time-weaver XML元素配置上下文范围的 LoadTimeWeaver 。所有JPA _681309自动接收这样的全球编织者。这是设置加载时织入器的首选方法,提供平台的自动检测(WebLogic,GlassFish,Tomcat,Resin,JBoss或VM代理)以及将weaver自动传播到所有weaver-aware bean:

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

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

<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 文件。)

<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 内部创建并使用一个 PersistenceUnitManager

20.5.2 基于JPA实现DAO:EntityManagerFactory和EntityManager

虽然EntityManagerFactory实例是线程安全的,但EntityManager实例却不是。注入的JPA EntityManager的行为类似于从应用程序服务器的JNDI环境获取的EntityManager,如JPA规范所定义。它将所有调用委托给当前的事务性EntityManager,如果有的话;否则,它会回退到每个操作新创建的EntityManager,实际上使其使用线程安全。

可以编写代码通过使用注入的 EntityManagerFactoryEntityManager 来对抗没有任何Spring依赖项的普通JPA。如果启用 PersistenceAnnotationBeanPostProcessor ,Spring可以在字段和方法级别理解 @PersistenceUnit@PersistenceContext 注释。使用 @PersistenceUnit 注释的普通JPA DAO实现可能如下所示:

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应用程序上下文。此外,DAO利用注释要求注入默认值 EntityManagerFactory

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

<beans>

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

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

</beans>

这样一个DAO的主要问题是它总是通过工厂创建一个新的 EntityManager 。您可以通过请求事务 EntityManager (也称为 "shared EntityManager" ,因为它是实际事务EntityManager的共享,线程安全的代理)而不是工厂来避免这种情况:

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。扩展的EntityManagers仅应用于有状态组件,例如驻留在会话中,EntityManager的生命周期不依赖于当前事务,而是完全取决于应用程序。


Method- and field-level Injection

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

类级别的注释怎么样?

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


注入的 EntityManager 是Spring管理的(知道正在进行的事务)。值得注意的是,即使新DAO实现使用方法级别注入 EntityManager 而不是 EntityManagerFactory ,由于注释的使用,应用程序上下文XML也不需要进行任何更改。

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

20.5.3 Spring驱动的JPA事务

强烈建议您阅读第17.5节“声明式事务管理”(如果您还没有这样做),以获得有关Spring声明式事务支持的更详细报道。

JPA的推荐策略是通过JPA的本机事务支持进行本地事务。 Spring的 JpaTransactionManager 提供了许多本地JDBC事务所知的功能,例如针对任何常规JDBC连接池的事务特定隔离级别和资源级只读优化(无XA要求)。

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

20.5.4 JpaDialect和JpaVendorAdapter

作为高级功能 JpaTransactionManagerAbstractEntityManagerFactoryBean 的子类支持自定义 JpaDialect ,要传递给 jpaDialect bean属性。 JpaDialect 实现可以启用Spring支持的一些高级功能,通常以特定于供应商的方式:

  • 应用特定的事务语义,例如自定义隔离级别或事务超时)

  • 检索事务JDBC Connection 以暴露给基于JDBC的DAO)

  • PersistenceExceptions 到Spring的高级翻译 DataAccessExceptions

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

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

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

20.5.5 使用JTA事务管理设置JPA

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

  • 底层JDBC连接池需要具备XA功能并与事务协调器集成。这在Java EE环境中通常很简单,只需通过JNDI公开不同类型的 DataSource 即可。有关详细信息,请查看应用程序服类似地,独立的事务协调器通常带有特殊的XA集成 DataSource 实现;再次检查其文档。

  • 需要为JTA配置JPA EntityManagerFactory 设置。这是特定于提供者的,通常通过在 LocalContainerEntityManagerFactoryBean 上指定为 "jpaProperties" 的特殊属性。在Hibernate的情况下,这些属性甚至是特定于版本的;请查看您的Hibernate文档以获取详细信息。

  • Spring的 HibernateJpaVendorAdapter 强制执行某些面向Spring的默认值,例如连接释放模式 "on-close" ,它匹配Hibernate在Hibernate 5.0中的默认值,但在5.1 / 5.2中不再存在。对于JTA设置,要么不声明 HibernateJpaVendorAdapter 开始,要么关闭其 prepareConnection 标志。或者,将Hibernate 5.2的 "hibernate.connection.handling_mode" 属性设置为 "DELAYED_ACQUISITION_AND_RELEASE_AFTER_STATEMENT" 以恢复Hibernate自己的默认值。有关WebLogic的相关说明,请参阅 Section 20.3.7, “Spurious application server warnings with Hibernate”

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

Updated at: 5 months ago
初始化依赖于数据库的其他组件Table of content21. 使用O / X Mappers编组XML
Comment
You are not logged in.

There are no comments.