Spring源码之事物 Transaction官方文档翻译

官方文档地址:

https://docs.spring.io/spring/docs/4.3.21.RELEASE/spring-framework-reference/htmlsingle/#transaction

介绍Spring框架事务管理

全面的事务支持是使用Spring框架的最令人信服的原因之一。Spring框架为事务管理提供了一致的抽象,提供了以下好处:

  • 跨不同事务API(如Java事务API (JTA)、JDBC、Hibernate、Java持久化API (JPA)和Java数据对象(JDO))的一致编程模型。
  • 支持声明式事务管理。
  • 用于编程事务管理的API比JTA等复杂事务API更简单。
  • 与Spring数据访问抽象的完美集成。

下面几节描述Spring框架的事务增值和技术。(本章还讨论了最佳实践、应用服务器集成和常见问题的解决方案。)

  • Spring框架事务支持模型的优点描述了为什么要使用Spring框架的事务抽象而不是EJB容器管理事务(CMT),或者选择通过Hibernate等专有API驱动本地事务。
  • 理解Spring Framework事务抽象概括了核心类,并描述了如何配置和从各种源获取数据源实例。
  • 将资源与事务同步描述应用程序代码如何确保资源被正确创建、重用和清理。
  • 声明式事务管理描述对声明式事务管理的支持。
  • 程序化事务管理包括对程序化(即显式编码)事务管理的支持。
  • 事务绑定事件描述如何在事务中使用应用程序事件。

Spring框架事务支持模型的优点

传统上,Java EE开发人员有两种事务管理选择:全局事务或本地事务,这两种事务都有深刻的局限性。在接下来的两个部分中,将回顾全局和本地事务管理,然后讨论Spring框架的事务管理支持如何解决全局和本地事务模型的局限性。

全局事务使您能够处理多个事务资源,通常是关系数据库和消息队列。应用服务器通过JTA管理全局事务,JTA使用起来很麻烦(部分原因是它的异常模型)。此外,JTA UserTransaction通常需要来自JNDI,这意味着您还需要使用JNDI才能使用JTA。显然,使用全局事务会限制应用程序代码的任何潜在重用,因为JTA通常只在应用服务器环境中可用。

以前,使用全局事务的首选方式是通过EJB CMT(容器管理事务):CMT是声明式事务管理的一种形式(区别于编程事务管理)。EJB CMT消除了对与事务相关的JNDI查找的需要,当然EJB本身的使用也需要JNDI的使用。它消除了大部分但不是全部编写Java代码来控制事务的需要。显著的缺点是CMT与JTA和应用服务器环境绑定在一起。而且,只有在选择在EJB中实现业务逻辑时,或者至少在事务EJB facade之后,它才可用。EJB的缺点通常是如此之大,以至于这不是一个有吸引力的命题,尤其是在面对声明性事务管理的强制性替代方案时。

本地事务是特定于资源的,例如与JDBC连接关联的事务。本地事务可能更容易使用,但是有很大的缺点:它们不能跨多个事务资源工作。例如,使用JDBC连接管理事务的代码不能在全局JTA事务中运行。由于应用服务器不涉及事务管理,因此它不能帮助确保跨多个资源的正确性。(值得注意的是,大多数应用程序使用单个事务资源。)另一个缺点是本地事务对编程模型具有入侵性。

Spring解决了全局和本地事务的缺点。它使应用程序开发人员能够在任何环境中使用一致的编程模型。您只需编写一次代码,它就可以从不同环境中的不同事务管理策略中获益。Spring框架提供了声明性和程序性事务管理。大多数用户更喜欢声明式事务管理,这在大多数情况下是推荐的。

使用编程式事务管理,开发人员可以使用Spring Framework事务抽象,它可以在任何底层事务基础架构上运行。使用首选的声明式模型,开发人员通常只编写很少或根本不编写与事务管理相关的代码,因此不依赖于Spring Framework事务API或任何其他事务API。

您是否需要一个用于事务管理的应用服务器?

当企业Java应用程序需要应用服务器时,Spring Framework的事务管理支持改变了传统的规则。

 

特别地,您不需要仅仅为了通过ejb进行声明性事务而使用应用服务器。事实上,即使您的应用服务器具有强大的JTA功能,您也可能认为Spring框架的声明性事务提供了比EJB CMT更强大的功能和更高效的编程模型。

 

通常,只有当应用程序需要跨多个资源处理事务时,才需要应用程序服务器的JTA功能,这对于许多应用程序来说不是必需的。许多高端应用程序使用一个高度可伸缩的数据库(例如Oracle RAC)。独立事务管理器(如Atomikos事务和JOTM)是其他选项。当然,您可能需要其他应用服务器功能,如Java Message Service (JMS)和Java EE连接器体系结构(JCA)。

 

Spring框架让您可以选择何时将应用程序扩展到完全加载的应用服务器。使用EJB CMT或JTA的唯一替代方法是使用本地事务(如JDBC连接上的事务)编写代码的日子已经一去不复返了,如果需要这些代码在全局容器管理的事务中运行,则需要进行大量的重新工作。使用Spring框架,只需要更改配置文件中的一些bean定义,而不需要更改代码。

理解Spring框架事务抽象

Spring事务抽象的关键是事务策略的概念。事务策略由org.springframework.transaction.PlatformTransactionManager接口定义

public interface PlatformTransactionManager {

    TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;

    void commit(TransactionStatus status) throws TransactionException;

    void rollback(TransactionStatus status) throws TransactionException;
}

这主要是一个服务提供者接口(SPI),尽管它可以从应用程序代码中以编程方式使用。因为PlatformTransactionManager是一个接口,所以可以根据需要轻松地模拟或存根化它。它不绑定到JNDI之类的查找策略。PlatformTransactionManager实现与Spring Framework IoC容器中的任何其他对象(或bean)一样定义。仅这一点就使Spring框架事务成为一个有价值的抽象,即使在使用JTA时也是如此。与直接使用JTA相比,可以更容易地测试事务性代码。

同样,与Spring的哲学相一致,任何PlatformTransactionManager接口的方法都可以抛出TransactionException(也就是说,它扩展了java.lang.RuntimeException类)。事务基础设施故障几乎总是致命的。在极少数情况下,应用程序代码实际上可以从事务失败中恢复,应用程序开发人员仍然可以选择捕获和处理TransactionException。重要的一点是,开发人员并不是被迫这样做的。

方法返回一个TransactionStatus对象,具体取决于一个TransactionDefinition参数。返回的TransactionStatus可能表示一个新事务,或者如果当前调用堆栈中存在匹配的事务,则可以表示一个现有事务。后一种情况的含义是,与Java EE事务上下文一样,TransactionStatus与执行线程相关联。

TransactionDefinition接口指定:

  • 传播:通常,在事务范围内执行的所有代码都将在该事务中运行。但是,您可以选择在事务上下文已经存在时执行事务方法的事件中指定行为。例如,代码可以在现有事务中继续运行(通常情况下);或者可以挂起现有事务并创建一个新事务。Spring提供了EJB CMT中熟悉的所有事务传播选项。要了解Spring中事务传播的语义,请参见17.5.7节“事务传播”。
  • 隔离:此事务与其他事务的工作隔离的程度。例如,这个事务可以看到来自其他事务的未提交的写吗?
  • 超时:该事务在超时并被底层事务基础设施自动回滚之前运行的时间。
  • 只读状态:当代码读取但不修改数据时,可以使用只读事务。只读事务在某些情况下可能是一种有用的优化,例如在使用Hibernate时。

这些设置反映了标准事务概念。如果需要,请参阅讨论事务隔离级别和其他核心事务概念的参考资料。理解这些概念对于使用Spring框架或任何事务管理解决方案都是必不可少的。

TransactionStatus接口为事务代码提供了一种控制事务执行和查询事务状态的简单方法。这些概念应该是熟悉的,因为它们对于所有事务api都是通用的:

public interface TransactionStatus extends SavepointManager {

    boolean isNewTransaction();

    boolean hasSavepoint();

    void setRollbackOnly();

    boolean isRollbackOnly();

    void flush();

    boolean isCompleted();

}

无论您在Spring中选择声明式事务管理还是程序化事务管理,定义正确的PlatformTransactionManager实现都是绝对必要的。您通常通过依赖注入定义此实现。

PlatformTransactionManager实现通常需要了解它们工作的环境:JDBC、JTA、Hibernate等等。下面的例子展示了如何定义本地PlatformTransactionManager实现。(本例使用普通JDBC。)

定义JDBC数据源:

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

然后,相关的PlatformTransactionManager bean定义将具有对数据源定义的引用。它看起来是这样的:

<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>

如果您在Java EE容器中使用JTA,那么您将使用通过JNDI获得的容器数据源,以及Spring的JtaTransactionManager。JTA和JNDI查找版本是这样的:

<?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:jee="http://www.springframework.org/schema/jee"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/jee
        http://www.springframework.org/schema/jee/spring-jee.xsd">

    <jee:jndi-lookup id="dataSource" jndi-name="jdbc/jpetstore"/>

    <bean id="txManager" class="org.springframework.transaction.jta.JtaTransactionManager" />

    <!-- other <bean/> definitions here -->

</beans>

JtaTransactionManager不需要了解数据源或任何其他特定资源,因为它使用容器的全局事务管理基础设施。

上面数据源bean的定义使用来自jee名称空间的<jndi-lookup/>标记。有关基于模式的配置的更多信息,请参见第41章基于XML模式的配置,以及关于<jee/>标记的更多信息,请参见标题为41.2.3节的“jee模式”一节。

您还可以轻松地使用Hibernate本地事务,如下面的示例所示。在这种情况下,您需要定义Hibernate LocalSessionFactoryBean,您的应用程序代码将使用它来获取Hibernate会话实例。

数据源bean定义将类似于前面显示的本地JDBC示例,因此在下面的示例中不会显示。

如果任何非jta事务管理器使用的数据源是通过JNDI查找并由Java EE容器管理的,那么它应该是非事务性的,因为将管理事务的是Spring框架,而不是Java EE容器。

正如DataSourceTransactionManager需要对数据源的引用一样,HibernateTransactionManager需要对SessionFactory的引用。

<bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
    <property name="dataSource" ref="dataSource"/>
    <property name="mappingResources">
        <list>
            <value>org/springframework/samples/petclinic/hibernate/petclinic.hbm.xml</value>
        </list>
    </property>
    <property name="hibernateProperties">
        <value>
            hibernate.dialect=${hibernate.dialect}
        </value>
    </property>
</bean>

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

如果您正在使用Hibernate和Java EE容器管理的JTA事务,那么您应该简单地对JDBC使用与前一个JTA示例相同的JtaTransactionManager。

<bean id="txManager" class="org.springframework.transaction.jta.JtaTransactionManager"/>

如果您使用JTA,那么无论您使用什么数据访问技术,无论是JDBC、Hibernate JPA还是任何其他受支持的技术,您的事务管理器定义看起来都是一样的。这是因为JTA事务是全局事务,它可以征募任何事务资源。

在所有这些情况下,应用程序代码都不需要更改。您可以仅通过更改配置来更改事务的管理方式,即使更改意味着从本地事务转移到全局事务,或者相反。

使资源与事务同步

现在应该清楚如何创建不同的事务管理器,以及如何将它们链接到需要同步到事务的相关资源(例如DataSourceTransactionManager到JDBC数据源,HibernateTransactionManager到Hibernate SessionFactory,等等)。本节描述应用程序代码如何直接或间接地使用JDBC、Hibernate或JDO等持久性API来确保正确地创建、重用和清理这些资源。本节还讨论了如何通过相关的PlatformTransactionManager(可选地)触发事务同步。

高级的同步方法:

首选的方法是使用Spring最高级别的基于模板的持久性集成api,或者使用具有事务感知的工厂bean或代理的本地ORM api来管理本地资源工厂。这些事务感知解决方案在内部处理资源创建和重用、清理、资源的可选事务同步和异常映射。因此,用户数据访问代码不必处理这些任务,而可以只关注于非样板持久性逻辑。通常,您可以使用本机ORM API,或者通过使用JdbcTemplate采用JDBC访问的模板方法。这些解决方案将在本参考文档的后续章节中详细介绍。

低级的同步方法:

诸如DataSourceUtils(用于JDBC)、EntityManagerFactoryUtils(用于JPA)、SessionFactoryUtils(用于Hibernate)、PersistenceManagerFactoryUtils(用于JDO)等类存在于较低的级别。当您希望应用程序代码直接处理本地持久性API的资源类型时,您可以使用这些类来确保获得适当的Spring框架管理实例、事务(可选地)同步以及流程中发生的异常被正确地映射到一致的API。

例如,在JDBC的情况下,不使用在数据源上

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值