一、什么是事务传播机制?

  事务传播机制,顾名思义就是多个事务方法之间调用,事务如何在这些方法之间传播,是重新创建事务还是使用父方法的事务,父方法的回滚对子方法的事务是否有影响等等,这些都是事务传播机制决定的。

二、Spring 事务传播特性有哪些?

  多个事务方法相互调用时,事务如何在这些方法之间进行传播呢?Spring 提供了 7 种不同的传播特性,来保证事务的正常进行。源码如下所示。

package org.springframework.transaction.annotation;public enum Propagation {REQUIRED(0),SUPPORTS(1),MANDATORY(2),REQUIRES_NEW(3),NOT_SUPPORTED(4),NEVER(5),NESTED(6);private final int value;private Propagation(int value) {this.value = value;}public int value() {return this.value;}
}

  Propagation(传播特性)有以下选项可供使用:REQUIRED、SUPPORTS、MANDATORY、REQUIRES_NEW、NOT_SUPPORTED、NEVER、NESTED。
  在陈述传播特性之前,以如下伪代码为例,陈述几个术语。

ServiceA.methodA(required){// methodA 方法的业务ServiceB.methodB(required);
}

  ServiceA 中的 methodA 方法调用 ServiceB 中的 methodB 方法,ServiceA.methodA 方法的事务被称为 “当前事务”(current transaction,有些文章中也称为父事务或外部事务),ServiceB.methodB 方法的事务被称为子事务(或内部事务)。上述伪代码设置了当前事务传播特性为 REQUIRED,子事务传播特性为 REQUIRED。
  Spring 的几种事务传播特性含义如下(均站在子方法的立场而言,即上述伪代码中的 methodB)。

  • REQUIRED
    这是 Spring 默认的传播特性。如果当前没有事务,则新建一个事务;如果当前存在事务,则加入这个事务。这是实际项目开发中最常见的配置。
    例如,ServiceA.methodA 的事务传播特性定义为 Propagation.REQUIRED,ServiceB.methodB 的事务传播特性定义为 Propagation.REQUIRED,那么由于执行 ServiceA.methodA 的时候,ServiceA.methodA 已经起了事务,这时调用 ServiceB.methodB,ServiceB.methodB 看到自己已经运行在 ServiceA.methodA 的方法内部,就不再起新的事务,而是加入当前事务。这样,在 ServiceA.methodA 或者在 ServiceB.methodB 内的任何地方出现异常,事务都会被回滚,即使 ServiceB.methodB 的事务已经被提交,但是 ServiceA.methodA 在接下来异常要回滚,ServiceB.methodB 也要回滚。

  • SUPPORTS
    如果存在当前事务,即以事务的形式运行,如果当前没有事务,则以非事务的方式执行。

  • MANDATORY
    存在当前事务,则加入当前事务,如果没有当前事务,就抛出异常。
    必须在一个事务中运行,也就是说,它只能被一个父事务调用,否则子方法就抛出异常。

  • REQUIRES_NEW
    创建一个新事务,如果存在当前事务,则把当前事务挂起。例如,我们设计 ServiceA.methodA 的事务传播特性为 Propagation.REQUIRED,ServiceB.methodB 的事务传播特性为 Propagation.REQUIRES_NEW,那么当执行到 ServiceB.methodB 的时候,ServiceA.methodA 所在的事务就会挂起,ServiceB.methodB 会起一个新的事务,等到 ServiceB.methodB 的事务完成以后,ServiceA.methodA 所在的事务才继续执行。它与 REQUIRED 的事务区别在于事务的回滚程度。因为 ServiceB.methodB 是新起一个事务,相当于存在两个不同的事务。如果 ServiceB.methodB 已经提交,那么 ServiceA.methodA 失败回滚,ServiceB.methodB 是不会回滚的。如果 ServiceB.methodB 失败回滚,如果它抛出的异常被 ServiceA.methodA 捕获,ServiceA.methodA 事务仍然可能提交。

  • NOT_SUPPORTED
    以非事务方式执行操作,如果存在当前事务,就把当前事务挂起。例如,ServiceA.methodA 的事务传播特性是 Propagation.REQUIRED,而 ServiceB.methodB 的事务传播特性是 Propagation.NOT_SUPPORTED,那么当执行到 ServiceB.methodB 时,ServiceA.methodA 的事务挂起,而 ServiceB.methodB 以非事务的状态运行完,再继续 ServiceA.methodA 的事务。

  • NEVER
    不使用事务,以非事务方式执行。如果当前事务存在,则抛出异常,不能在事务中运行。假设 ServiceA.methodA 的事务传播特性是 Propagation.REQUIRED,而 ServiceB.methodB 的事务传播特性是 Propagation.NEVER ,那么 ServiceB.methodB 就要抛出异常了。

  • NESTED
    如果当前事务存在,就运行一个嵌套事务。如果不存在当前事务,就和 REQUIRED 一样新建一个事务。

三、部分事务的不同点
3.1 NESTED 和 REQUIRES_NEW 的区别

  REQUIRES_NEW 是新建一个事务,并且新开始的这个事务和当前事务无关,当前事务回滚,不会影响到 REQUIRES_NEW 事务。
  而 NESTED 是一个嵌套事务,是父事务的一个子事务。当前事务存在时,NESTED 会开启一个嵌套事务。
  在 NESTED 情况下,父事务回滚时,子事务也会回滚,而 REQUIRES_NEW 情况下,原有事务回滚,不会影响新开启的事务。

3.2 NESTED 和 REQUIRED 的区别

  REQUIRED 情况下,当前事务存在时,被调用方和调用方使用同一个事务,那么被调用方出现异常时,由于共用一个事务,所以无论是否 catch 异常,事务都会回滚。
  而 NESTED 情况下,被调用方发生异常时,调用方可以 catch 其异常,这样只有子事务回滚,父事务不会回滚。

四、测试验证

  为了验证传播特性的准确性,笔者采用 Springboot,分别编写了上述伪代码中的两个测试方法,即调用方 ServiceA.saveA 方法和被调用方 ServiceB.saveB 方法,详见下方代码。

package com.test.service;import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import com.test.entity.Stat;
import com.test.dao.sconl.StatMapper;import javax.annotation.Resource;@Service
public class ServiceA {@Resourcepublic StatMapper statMapper;@Resourcepublic ServiceB serviceB;@Transactional(value = "sconlTransactionManager", propagation = Propagation.REQUIRED)public void saveA() {Stat stat = new Stat();stat.setStatId("0001");stat.setStatNm("0001");int resultA = statMapper.insertSelective(stat);System.out.println(resultA);// 调用 B 服务的方法try {serviceB.saveB();} catch (Exception e) {e.printStackTrace();}// throw new RuntimeException("ExceptionA");}
}
package com.test.service;import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import com.test.entity.OrderInfo;
import com.test.dao.sconl.OrderMapper;import javax.annotation.Resource;@Service
public class ServiceB {@Resourcepublic OrderMapper orderMapper;@Transactional(value = "sconlTransactionManager", propagation = Propagation.NESTED)public void saveB() {OrderInfo orderInfo = new OrderInfo();orderInfo.setOrderId("orderId");int resultB = orderMapper.insertSelective(orderInfo);System.out.println(resultB);// throw new RuntimeException("ExceptionB");}
}

  上述两个测试类均采用声明式事务,通过 @Transactional 标签来声明事务,标签中的 value 属性设置事务管理器,propagation 属性设置事务传播特性,方法内部通过 throw new RuntimeException(“ExceptionB”) 来抛出异常。默认情况下,RuntimeException 异常会导致事务回滚。通过改变当前事务和子事务的传播特性,以及设置 saveA 和 saveB 方法内部是否抛出异常,来测试当前事务和子事务之间的事务传播特性。
  经过测试,结果和理论相符。下表总结下不同的事务传播特性和异常状态对结果的影响。
ServiceA.saveA() 以 Propagation.REQUIRED 修饰,ServiceB.saveB() 以表格中三种属性修饰,测试结果和一些关键性日志如下表所示(异常状态一列中,均以 A 表示 ServiceA.saveA() 方法,以 B 表示 ServiceB.saveB() 方法)。

异常状态 REQUIRED REQUIRES_NEW NESTED
A正常
B正常
A提交
B提交
A提交
B提交
A提交
B提交
A正常
B异常
A回滚
B回滚
A提交
B回滚
A提交
B回滚
Rolling back transaction to savepoint
A异常
B正常
A回滚
B回滚
A回滚
B提交
A回滚
B回滚
Releasing transaction savepoint
A异常
B异常
A回滚
B回滚
A回滚
B回滚
A回滚
B回滚

  测试时,将 logback 的应用日志级别和 root 日志级别设置为 DEBUG,则可以看到具体的事务信息。
  将子事务设置为 REQUIRED 时,一些关键性日志如下所示:

Fetched SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@d093edf] from current transaction

  将子事务设置为 REQUIRES_NEW 时,一些关键性日志如下所示:

Suspending current transaction, creating new transaction with name [com.test.service.ServiceB.saveB]Resuming suspended transaction after completion of inner transaction

  将子事务设置为 NESTED 时,一些关键性日志如下所示:

Creating nested transaction with name [com.test.service.ServiceB.saveB]Releasing transaction savepoint
五、测试时踩过的坑

  笔者在实际代码中测试时,由于踩了一些坑,导致事务传播效果和理论不符,现在简单记录下来。

5.1 多数据源情况下未指明事务管理器

  笔者在多数据源项目中做测试,开始时未在事务标签中通过 value 属性指明事务管理器,出现了和理论不符的测试效果。后来在父方法和子方法中均指明了实际的数据源事务管理器,测试效果才和理论相符。

5.2 方法修饰符设置成 private

  开始时部分方法的修饰符设置为 private,导致事务失效,效果和理论不符。为了能被 AOP 事务增强,方法修饰符须为 public。

5.3 父事务方法未捕获子事务方法的异常

  开始时,父方法内部调用子方法时,未通过 try - catch 捕获子方法的异常,导致效果和理论不符。后来在 ServiceA.saveA() 方法内部调用 ServiceB.saveB() 时,用 try - catch 捕获了子事务方法的异常,测试效果终和理论相符了。

文章参考:
  • 一文搞懂spring boot本地事务@Transactional参数
  • SpringBoot事务传播属性测试
  • Spring事务的传播特性引发的一场血案

Spring 的事务传播机制相关推荐

  1. spring上下文是什么意思_Java程序员只会CRUD连Spring事务传播机制都不懂?

    AQS到底有什么用?难道就真的只是为了面试吗? 当然不是说AQS没用,如果你不是做基础架构或者中间件开发,你很难感受到AQS的威力.当然,学习很多时候,需要的是正向反馈,学了太多造火箭的东西,面试完就 ...

  2. 原创 | CRUD更要知道的Spring事务传播机制

    来自:肥朝 AQS到底有什么用?难道就真的只是为了面试吗? 当然不是说AQS没用,如果你不是做基础架构或者中间件开发,你很难感受到AQS的威力.当然,学习很多时候,需要的是正向反馈,学了太多造火箭的东 ...

  3. Spring事务与事务传播机制

    目录 1.事务的基本概念 2.Spring事务的实现 3.事务隔离级别 4.事务传播机制 1.事务的基本概念 关于事务的一些基础概念我已经在MYSQL中讲解过了,有不了解的可以移步至此篇文章: MyS ...

  4. Spring事务传播机制大白话(使用springboot,注解演示)

    1. 我对传播机制的理解 为什么需要传播机制? 因为事务之间可能存在相互调用,例如service业务层的方法存在相互调用,如果相互调用的方法都开启了事务(对应到springboot就是在方法上要添加@ ...

  5. 【Spring事务】事务和事务传播机制

    事务 事务主要有三种操作: 开始事务 start transaction 提交事务 commit 回滚事务 rollback Spring 中事务的实现 Spring 中的事务操作分为两类: ⼿动操作 ...

  6. SpringBoot事务传播机制

    6.Spring的事务传播机制: 1)定义:咱们之前所说的事务,都是针对一个方法的,咱们的Spring事务传播机制,不再是单个的事务了,咱们的Spring事务传播机制定义了包含多个方法相互调用的时候, ...

  7. Spring事务传播机制和隔离级别

    Spring有5种隔离级别,7种传播行为.这是面试常问的内容,也是代码中经常碰到的知识点.这些知识枯燥而且乏味,其中有些非常的绕.如果栽在这上面,就实在是太可惜了. @Transactional(is ...

  8. java spring 事务传播_spring事务传播机制实例讲解

    天温习spring的事务处理机制,总结如下 对于SQL事务的概念以及ACID性质,可以参见我的另一篇博文 http://kingj.iteye.com/admin/blogs/1675011 spri ...

  9. Spring 事务传播机制 实例讲解

    事务传播机制 对于SQL事务的概念以及ACID性质,可以参见我的另一篇博文 http://kingj.iteye.com/admin/blogs/1675011 spring的管理的事务可以分为如下2 ...

  10. Spring事务传播机制以及事务嵌套

    Spring事务传播机制以及事务嵌套 Spring事务传播机制 事务嵌套场景 情景0: 场景1:不同类中,开启事务的方法调用没有开启事务的方法 场景2:不同类中,methodA方法嵌套methodB方 ...

最新文章

  1. springboot 订单重复提交_Spring Boot (一) 校验表单重复提交
  2. 在面向数据流的设计方法中,一般把数据流图中的数据流划分为 (16) 两种。 答案:C
  3. 用一个创业故事串起操作系统原理(一)
  4. Python中将数据矢量化运算所带来的时间加快
  5. 【Python】 Spyder
  6. Visual Studio 2005 Service Pack 1 正式版发布
  7. python 装饰函数
  8. 12c分页查询特性FETCH FIRST ROWS,OFFSET ROWS FETCH NEXT ROW LIMIT Clause子句
  9. 微服务框架Demo.MicroServer运行手册
  10. CF932E-Team Work【斯特林数,组合数学】
  11. c#做端口转发程序支持正向连接和反向链接
  12. php 开启coredump,GDB分析PHP连接Memcached 导致coredump问题
  13. 如何估算太坊交易的gas消耗量
  14. [转载] python 运算符重载有什么用_Python运算符重载用法实例分析
  15. 3dmax2020卸载/安装失败/如何彻底卸载清除干净3dmax2020注册表和文件的方法
  16. Java项目:SSM问卷调查系统
  17. python 档案管理系统_用Python编写人才档案管理系统?
  18. 如何下载windows自带的锁屏壁纸
  19. 网站的robots 文件写法 附自动生成工具网址
  20. 对 COMP 通证经济模型的一般性评价

热门文章

  1. linux系统sdio接口wifi编程,3个SDIO接口WiFi模块/WiFi+蓝牙组合模块介绍-SKYLAB
  2. CentOS7.5.1804 Minimal 静默安装oracle 12c R1企业版
  3. Chapter3 Language Basics
  4. 国内外优秀呼叫中心系统简介
  5. ui设计网站资料大全,你想要的都有
  6. python正则匹配中文
  7. vector初始化方法(待补充)
  8. python网络爬虫最细致讲解之漫画抓取【细!!!】
  9. 计算机excel高级筛选操作大全,Excel高级筛选的7个使用实例,含不等于空、多条件、筛选到另一表格、用通配符*/?或公式组合条件...
  10. 2021-01-18