[JavaEE - JPA] 3. Spring Framework中的事务管理

前文讨论了事务划分(Transaction Demarcation)在EJB中是如何实现的,本文继续介绍在Spring Framework中是如何完成事务划分的。

我们已经知道了当采用Container事务类型的时候,事务划分主要有以下两种方案(参考这里):

  1. 使用JTA接口在应用中编码完成显式划分
  2. 在容器的帮助下完成自动划分

在使用JavaEE的EJB规范时,这两种方案分别被实现为BMT以及CMT,关于BMT和CMT在上一篇文章中有比较详尽的讨论(参考这里)。

那么对于Spring Framework而言,又是如何来实现上述两种方案的呢。

Spring Framework的主要优势之一就是屏蔽了底层容器的很多实现细节,它甚至会在JavaEE的标准之上进行一些封装,来最大程度地做到平台无关,容器无关。在使用Spring Framework开发JavaEE应用的时候,对底层容器的依赖并不明显。因此它并没有像EJB那样当采用基于Container的事务类型时,严重地依赖于JTA这一规范。它通过提供下面两种实现方案完成了对于事务的划分:

  1. 声明式的事务管理(Declarative Transaction Management)
  2. 编程式的事务管理(Programmatic Transaction Management)

虽然名字叫法不同,但是本质是差不多的。

第一种,声明式的事务管理,目的就是要帮助开发人员完成事务的自动划分。这一点和EJB CMT非常类似。第二种,编程式的事务管理,目的就是让开发人员能够有办法自行控制事务应该如何进行。这一点和EJB BMT非常类似。

Spring Framework事务管理概要

在介绍具体的声明式以及编程式这两种事务管理方案之前,还是需要简单介绍一下Spring Framework是如何进行事务管理的。

在传统的JavaEE中,开发人员一般可以采用两种事务类型:

  1. Resource-local 本地事务
  2. Global 全局事务 (一般需要应用服务器提供的容器环境,所以也称为Container事务)

对于本地事务而言,它没有办法针对多个事务性资源,往往只能针对单一的事务资源。而且它也过于底层,对于业务逻辑的侵入性很强,这一点写过基于JDBC应用的同学都知道,20行代码里面可能只有5行是业务相关的,剩下15行都用来处理事务和相关异常了。所以这样的代码很难维护,看上去也不那么优雅。

对于全局事务而言,它克服了本地事务的缺点,能够处理多个事务性资源(典型的比如数据库,消息队列等)。但是它依赖于笨重的JTA(Java Transaction API),这一套和事务相关的API用起来也是让人叫苦不迭,和JDBC类似,都有太过底层的问题。更重要的是,JTA一般是应用服务器才会提供的服务,因此使用全局事务的条件也算是比较苛刻。

所以针对以上的种种痛点,Spring Framework建立了一套关于事务的抽象。让开发人员可以在任何环境中方便地使用事务来管理资源。甚至在一般情况下连应用服务器也不需要了,使用一般的Web容器即可,比如流行的Tomcat(只有在需要同时处理多个事务性资源的情况下,才可能需要使用应用服务器,但是一般的应用显然没有那么复杂)。

Spring Framework对事务的抽象

Spring Framework通过引入一个名为事务策略(Transaction Strategy)的概念来建立这个抽象。具体而言,表现为下面这个接口:

public interface PlatformTransactionManager {
    TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;
    void commit(TransactionStatus status) throws TransactionException;
    void rollback(TransactionStatus status) throws TransactionException;
}

org.springframework.transaction.PlatformTransactionManager接口本质上是一个服务提供接口(Service Provider Interface, SPI)。这也算是实现系统扩展性的一种经典设计模式了,具体可以参考维基百科。比如我们所熟知的JDBC就是依赖于这一模式,各种数据库厂商都实现了JDBC SPI中的功能从而使他们的数据库能够和Java应用通信。

接口中定义的每个方法都可能会抛出一个名为TransactionException的异常,这个异常不像是JTA的相关接口中定义的那些异常基本上都是受检异常(Checked Exception),而TransactionException是一个运行时异常(Runtime Exception)。这也反映出了Spring Framework的设计原则,尽量不给开发人员添乱。毕竟处理事务相关的异常可不是一门轻松活。一般而言都是直接抛出去,谁有能力处理交给谁吧。所以这里将异常定义为运行时异常默认了开发人员不会处理它,当然如果有能力处理的话加上try…catch语句就好了。

下面简要介绍一下上述接口中的三个方法:

TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;

这里有出现了两个新概念。

接受的参数是TransactionDefinition接口,返回的对象是TransactionStatus接口。

TransactionDefinition接口的文档中,有这么一段话:

Interface that defines Spring-compliant transaction properties.

Based on the propagation behavior definitions analogous to EJB CMT attributes.

翻译一下就是:这个接口定义了Spring兼容的事务属性。这些属性类似于EJB CMT中关于事务传播(Transaction Propagation)行为的定义。而这个事务传播实际上就是指的@TransactionAttribute中定义的那些个属性,诸如MANDATORYREQUIREDREQUIRES_NEW等,详情可以参考这里

知道了这一点,再来看看里面定义了些什么:

public interface TransactionDefinition {
    // 传播属性
    int PROPAGATION_REQUIRED = 0;
    int PROPAGATION_SUPPORTS = 1;
    int PROPAGATION_MANDATORY = 2;
    int PROPAGATION_REQUIRES_NEW = 3;
    int PROPAGATION_NOT_SUPPORTED = 4;
    int PROPAGATION_NEVER = 5;
    int PROPAGATION_NESTED = 6;
    // 隔离属性
    int ISOLATION_DEFAULT = -1;
    int ISOLATION_READ_UNCOMMITTED = Connection.TRANSACTION_READ_UNCOMMITTED;
    int ISOLATION_READ_COMMITTED = Connection.TRANSACTION_READ_COMMITTED;
    int ISOLATION_REPEATABLE_READ = Connection.TRANSACTION_REPEATABLE_READ;
    int ISOLATION_SERIALIZABLE = Connection.TRANSACTION_SERIALIZABLE;
    // 超时属性
    int TIMEOUT_DEFAULT = -1;

    // 行为
    int getPropagationBehavior();
    int getIsolationLevel();
    int getTimeout();
    boolean isReadOnly();
    String getName();
}

可见,这个接口里面定义了各种属性,同时也定义了获取一个事务属性的方法。所以将该接口作为上述getTransaction方法的参数,目的就很清晰了:根据所描述事务的属性来获取具体的事务。就好比传入一个config对象,得到一个符合该config定义的对象那样。

然后,方法返回的TransactionStatus又是啥呢:

public interface TransactionStatus extends SavepointManager, Flushable {
    boolean isNewTransaction();
    boolean hasSavepoint();
    void setRollbackOnly();
    boolean isRollbackOnly();
    void flush();
    boolean isCompleted();
}

这就很明显了,它和之前介绍过的JTA提供用于实现BMT的UserTransaction相似度很高。它代表的就是一个当前执行线程所关联的一个具体事务对象。根据TransactionDefinition所定义的事务属性,这个具体的事务对象可以是一个刚刚创建的(当传播属性定义为PROPAGATION_REQUIRES_NEW时),也可以是复用的一个事务对象(当传播属性定义为PROPAGATION_SUPPORTS或者PROPAGATION_REQUIRED并且确实运行在事务环境中时)。

PlatformTransactionManager接口中剩下的两个方法commit()rollback()看名字就知道它们怎么用了,因此不再一一介绍了。

PlatformTransactionManager接口的实现是需要根据具体的环境而被注入到运行时环境中的,比如JTA,JDBC,Hibernate等。关于具体的注入方法可以参考Spring Framework的相关文档,这里就不再细说。

声明式的事务管理(Declarative Transaction Management)

值得一体的是,Spring Framework是通过AOP(面向切面编程,Aspect-Oriented Programming)技术来实现声明式的事务管理的。关于AOP,如果想要了解更多可以参考Wiki

其实Spring Framework在实现声明式的事务管理时,多少也借鉴了一些EJB CMT的实现方式,取其精华去其糟粕。那么将它和EJB CMT比较的话,主要有以下几个方面的提高:

  1. 不再像EJB CMT那样局限于JTA。Spring Framework提供的声明式事务管理能够运行在几乎任何主流环境下,比如JTA,JDBC,Hibernate等。开发人员所需要做的只是根据底层环境提供相应的配置即可。
  2. 可以对任何类使用声明式事务管理,而不像EJB CMT那样只能对有限的几种类型,比如@Stateful@Stateless等会话Bean。
  3. 声明式的回滚规则,比如指定抛出了哪些类型的异常时才发生回滚。这一特性是EJB CMT中不存在的。
  4. 通过AOP技术定制化事务行为,比如在事务发生回滚的时候执行自定义代码。而在EJB CMT中对于回滚你能做的仅仅是调用setRollbackOnly()

和EJB CMT比较相近的一点是,它们都在发生了运行时异常(Runtime Exception)时会触发回滚操作,但是对于受检异常(Checked Exception)是不会主动回滚的,一般的理解是需要开发人员来处理,在catch语句中显式地执行回滚操作。

说的这么好听,那么如何使用声明式的事务管理呢。其实用法很简单,就两个步骤(以注解配置方式为例):

1. 在@Configuration的JavaConfig类中使用@EnableTransactionManagement

2. 在需要使用事务的类或者方法上使用@Transactional

要想理解它是如何实现的,首先需要了解AOP Proxy这一概念。也就是说,在开发人员声明了@Transactional之后,Spring Framework会利用AOP技术生成一个代理对象(Proxy),这个代理对象使用TransactionInterceptor并结合PlatformTransactionManager来实现事务的相关功能。

可以用下面这张图来表示:

在Spring Framework没有介入之前,只存在上图中的调用者和目标方法这两个部分。而在介入后利用AOP以及动态代理技术首先为方法所在对象创建了一个代理对象,然后根据配置生成各种AOP任务,如上图中的事务Advisor以及其它类型的Advisor(日志任务等)。然后在真正发生方法调用的时候,调用的顺序如上图中的数字1-8。在事务Advisor被执行的时候(步骤2)才会真正创建事务,然后在步骤4执行的是业务逻辑。随后执行流程开始依次返回,到步骤7发生的时候事务会根据其是否成功而提交或者是回滚。

对于具体的基于XML以及注解的配置方法,可以查看Spring Framework Transaction部分的相关文档

编程式的事务管理(Programmatic Transaction Management)

Spring Framework提供了两种方法来支持编程式的事务管理:

  1. TransactionTemplate
  2. PlatformTransactionManager

推荐使用第一种方式。TransactionTemplate在形式上类似于Spring JDBC中提供的JdbcTemplate,封装了很多模板代码,让开发人员可以专注到业务逻辑的开发上。第二种类似于JTA中提供的UserTransaction,但是简化了部分异常处理。

利用TransactionTemplate完成编程式事务管理

下面是一段使用TransactionTemplate完成编程式事务管理的代码片段(下面的摘自Spring Framework官方文档):

public class SimpleService implements Service {
    // 共享的TransactionTemplate实例
    private final TransactionTemplate transactionTemplate;
    // 利用构造器注入将PlatformTransactionManager的实现注入到类中
    public SimpleService(PlatformTransactionManager transactionManager) {
        Assert.notNull(transactionManager, "The ‘transactionManager‘ argument must not be null.");
        this.transactionTemplate = new TransactionTemplate(transactionManager);
    }
    public Object someServiceMethod() {
        return transactionTemplate.execute(new TransactionCallback() {
            // 此方法中的代码会在事务上下文中执行
            public Object doInTransaction(TransactionStatus status) {
                updateOperation1();
                return resultOfUpdateOperation2();
            }
        });
    }
}

使用了回调的风格完成了编程式的事务管理,其中比较关键的是TransactionCallback类的匿名实现,它作为参数传入到TransactionTemplate的execute方法中。

如果事务相关代码并没有返回值,那么可以使用不带参数的TransactionCallbackWithoutResult类的匿名实现。

transactionTemplate.execute(new TransactionCallbackWithoutResult() {
    protected void doInTransactionWithoutResult(TransactionStatus status) {
        try {
            updateOperation1();
            updateOperation2();
        } catch (SomeBusinessExeption ex) {
            // 发生异常时,回滚事务
            status.setRollbackOnly();
        }
    }
});

利用PlatformTransactionManager完成编程式事务管理

可以在获取到PlatformTransactionManager的实现后通过下面的逻辑完成事务管理:

DefaultTransactionDefinition def = new DefaultTransactionDefinition();
// 只有在编程式事务管理中才能设置事务的名称
def.setName("SomeTxName");
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
TransactionStatus status = txManager.getTransaction(def);
try {
    // 执行业务逻辑
}
catch (MyException ex) {
    txManager.rollback(status);
    throw ex;
}
txManager.commit(status);

总结

本文简要介绍了Spring Framework中的事务管理。它是如何创建出和具体底层环境无关的抽象层,以及如何使用声明式/编程式来完成事务管理的。介绍的比较简要,资料参考自Spring Framework官方文档。如需要具体的配置方法和更多的例子,可以直接参考上述文档。

时间: 2024-08-02 07:00:12

[JavaEE - JPA] 3. Spring Framework中的事务管理的相关文章

Spring Boot中的事务管理

原文  http://blog.didispace.com/springboottransactional/ 什么是事务? 我们在开发企业应用时,对于业务人员的一个操作实际是对数据读写的多步操作的结合.由于数据操作在顺序执行的过程中,任何一步操作都有可能发生异常,异常会导致后续操作无法完成,此时由于业务逻辑并未正确的完成,之前成功操作数据的并不可靠,需要在这种情况下进行回退. 事务的作用就是为了保证用户的每一个操作都是可靠的,事务中的每一步操作都必须成功执行,只要有发生异常就回退到事务开始未进行

企业分布式微服务云SpringCloud SpringBoot mybatis (十七)Spring Boot中的事务管理

快速入门 在Spring Boot中,当我们使用了spring-boot-starter-jdbc或spring-boot-starter-data-jpa依赖的时候,框架会自动默认分别注入DataSourceTransactionManager或JpaTransactionManager.所以我们不需要任何额外配置就可以用@Transactional注解进行事务的使用. 我们以之前实现的<用spring-data-jpa访问数据库>的示例Chapter3-2-2作为基础工程进行事务的使用常识

Spring中的事务管理

一.Spring事务管理用到的三个接口 a. PlatformTransactionManager 事务管理器 b. TransactionDefinition 事务定义信息(隔离.传播.超时.只读) c. TransactionStatus 事务具体的运行状态 二.Spring为不同的持久化框架提供了不同的PlatformTransactionManager接口实现 事务 说明 org.springframework.jdbc.datasource.DataSourceTransactionM

Spring Framework------&gt;version4.3.5.RELAESE-----&gt;Reference Documentation学习心得-----&gt;Spring Framework中的spring web MVC模块

spring framework中的spring web MVC模块 1.概述 spring web mvc是spring框架中的一个模块 spring web mvc实现了web的MVC架构模式,可以被用于开发web网站 spring web mvc 实现web网站的原理,如下图: 2.使用spring web mvc开发web应用的步骤 step1:在自己的工程中引入spring web mvc模块 step2:配置spring web mvc模块 中的DispatcherServlet,告

在Entity Framework中使用事务

小分享:我有几张阿里云优惠券,用券购买或者升级阿里云相应产品最多可以优惠五折!领券地址:https://promotion.aliyun.com/ntms/act/ambassador/sharetouser.html?userCode=ohmepe03 继续为想使用Entity Framework的朋友在前面探路,分享的东西虽然技术含量不高,但都是经过实践检验的. 在Entity Framework中使用事务很简单,将操作放在TransactionScope中,并通过Complete()方法提

转-Spring Framework中的AOP之around通知

Spring Framework中的AOP之around通知 http://blog.csdn.net/xiaoliang_xie/article/details/7049183 标签: springaop设计模式beanintegerclass 2011-12-07 11:39 6108人阅读 评论(0) 收藏 举报 在第一部分,您看到了如何使用Spring AOP来实现跟踪和记录方面.跟踪和记录都是"消极"方面,因为它们的出现并不会对应用程序的其他行为产生影响.它们都使用了消极的b

全面分析 Spring 的编程式事务管理及声明式事务管理--转

开始之前 关于本教程 本教程将深入讲解 Spring 简单而强大的事务管理功能,包括编程式事务和声明式事务.通过对本教程的学习,您将能够理解 Spring 事务管理的本质,并灵活运用之. 先决条件 本教程假定您已经掌握了 Java 基础知识,并对 Spring 有一定了解.您还需要具备基本的事务管理的知识,比如:事务的定义,隔离级别的概念,等等.本文将直接使用这些概念而不做详细解释.另外,您最好掌握数据库的基础知识,虽然这不是必须. 系统需求 要试验这份教程中的工具和示例,硬件配置需求为:至少带

分析 Spring 的编程式事务管理及声明式事务管理(转)

开始之前 关于本教程 本教程将深入讲解 Spring 简单而强大的事务管理功能,包括编程式事务和声明式事务.通过对本教程的学习,您将能够理解 Spring 事务管理的本质,并灵活运用之. 先决条件 本教程假定您已经掌握了 Java 基础知识,并对 Spring 有一定了解.您还需要具备基本的事务管理的知识,比如:事务的定义,隔离级别的概念,等等.本文将直接使用这些概念而不做详细解释.另外,您最好掌握数据库的基础知识,虽然这不是必须. 系统需求 要试验这份教程中的工具和示例,硬件配置需求为:至少带

Spring 的编程式事务管理及声明式事务管理

本文将深入讲解 Spring 简单而强大的事务管理功能,包括编程式事务和声明式事务.通过对本教程的学习,您将能够理解 Spring 事务管理的本质,并灵活运用之. Spring 事务属性分析 事务管理对于企业应用而言至关重要.它保证了用户的每一次操作都是可靠的,即便出现了异常的访问情况,也不至于破坏后台数据的完整性.就像银行的自助取款机,通常都能正常为客户服务,但是也难免遇到操作过程中机器突然出故障的情况,此时,事务就必须确保出故障前对账户的操作不生效,就像用户刚才完全没有使用过取款机一样,以保