手把手利用实战代码带你读懂Spring的事务传播行为
创始人
2025-05-29 23:32:14
0次

概念

首先简单了解一下 Spring 中事务传播行为是什么?听起来很高端,但是真正用起来的时候,稍有不慎,就会让自己陷入困境之中,所以在使用之前,我们必须要十分耐心认真的学习它。

从名字理解起来,事务传播行为,既然为传播就肯定发生在两个实体之间,否则单个实体又如何发生行为呢。通俗点讲就是“一个巴掌拍不响”。下面进入正规话题。

❝

事务传播行为主要用来描述由某一个事务传播行为修饰的方法被嵌套进另一个方法的事务中,该事务如何传播。这个概述可能不好理解,换句话就是当一个事务方法被另一个事务方法调用时,这个事务方法应该如何进行。

❞

下面用代码+文字说明解释上面的概念。

@Transaction(Propagation=XXX)
public void methodA(){methodB();//doSomething}@Transaction(Propagation=XXX)public void methodB(){//doSomething}

methodA 事务方法调用 methodB 事务方法时,methodB 是继续在调用者 methodA 的事务中运行呢,还是为自己开启一个新事务运行,这就是由 methodB 的事务传播行为决定的。

注意:methodA 和 methodB 都加了事务。methodA()也可以不用开启事务,某一个事务传播行为修饰的方法并不是必须要在开启事务的外围方法中调用。

Spring 中七种事务传播行为

通过上面伪代码加文字解释了解到事务传播行为的相关概念,下面就要学习事务传播行为的类型和运行机制。

Spring 中七种事务传播行为

验证

Propagation_Required

调用者方法不存在事务传播行为

「1.调用者方法内部存在异常时,被调用者方法均存在事务,那么结果如何呢?」

@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void insertStudent(StudentDo studentDo) {studentMapper.insertStudent(studentDo);System.out.println("----------------------->Student插入成功!");
}@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void insertClass(ClassDo classDo) {classMapper.insertClass(classDo);System.out.println("----------------------->Class插入成功!");
}

单元测试

// 单元测试
@SpringBootTest
@RunWith(SpringRunner.class)
public class PropagationTest {private final static StudentDo studentDo = new StudentDo();private final static ClassDo classDo = new ClassDo();static {studentDo.setClassId(1);studentDo.setStudentName("student1");studentDo.setAddress("测试");classDo.setClassName("class_1");classDo.setClassNo("Class01");}@Autowiredprivate StudentService studentService;@Autowiredprivate ClassService classService;@Testpublic void insertTest() {studentService.insertStudent(studentDo);classService.insertClass(classDo);}
}

Propagation_Required事务传播机制

结果:两条数据均被插入数据库。由于外部方法并没有开启事务,所以内部方法均在自己的事务提交或者回滚,因此外部方法中存在异常,内部方法事务不会回滚。

「2.被调用者均存在事务,而在被调用者中存在异常,那么结果如何?」

@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void insertStudent(StudentDo studentDo) {studentMapper.insertStudent(studentDo);System.out.println("----------------------->Student插入成功!");
}//此方法中抛出异常
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void insertClassByException(ClassDo classDo) throws CustomException {classMapper.insertClass(classDo);throw new CustomException();
}

单元测试代码

//单元测试代码
private final static StudentDo studentDo = new StudentDo();
private final static ClassDo classDo = new ClassDo();
static {studentDo.setClassId(2);studentDo.setStudentName("student2");studentDo.setAddress("测试2");classDo.setClassName("class_2");classDo.setClassNo("Class02");
}@Test
public void insertExceptionTest() throws CustomException {studentService.insertStudent(studentDo);classService.insertClassByException(classDo);
}

Propagation_Required

结果:第一数据成功插入,第二条数据因异常存在,事务回滚。内部方法均在各个的事务中运行,class 事务回滚,student 数据不会受到影响。

                       

结合 1 和 2 我们可以得出结论:

  1. 通过这两个方法我们证明了在外围方法未开启事务的情况下Propagation_Required修饰的内部方法会新开启自己的事务,且开启的事务相互独立,互不干扰。

  2. 调用者开启事务传播行为。

内部方法同上

//单元测试方法
@Test
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void insertInnerExceptionThrowsTest() throws CustomException {studentService.insertStudent(studentDo);classService.insertClassByException(classDo);
}

结果:内部方法虽然存在事务传播行为,但是外部方法也存在事务且使用Propagation.REQUIRED修饰,所有内部方法不会新建事务,直接运行在当前事务中,所以 student、class 均会被回滚。

  1. 调用者开启事务传播行为,但是捕获内部方法异常。

/*** 内部方法发生异常情况,外部方法即使捕获处理该异常,依然数据会被回滚*/
@Test
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void insertInnerExceptionTest() {studentService.insertStudent(studentDo);try {classService.insertClassByException(classDo);} catch (CustomException e) {e.printStackTrace();}
}

结果:外围方法开启事务,内部方法加入外围方法事务,内部方法抛出异常回滚,即使方法被 catch 不被外围方法感知,整个事务依然回滚。同 2 一样,调用者方法执行操作和被调用者中的方法操作结果均被回滚。

Propagation_Supports

@Transactional(propagation = Propagation.SUPPORTS, rollbackFor = Exception.class)
public void insertStudent(StudentDo studentDo) {studentMapper.insertStudent(studentDo);System.out.println("----------------------->Student插入成功!");
}@Test
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void insertSupportsTest() {studentService.insertStudent(studentDo);
}

解释:如果单纯的调用insertStudent()方法,则以非事务执行,即使后面存在异常情况,执行操作结果不会触发事务回滚机制。当调用insertSupportsTest()方法时,该方法以 REQUIRED 修饰,则会新建一个事务,内部调用insertStudent()方法,所以insertStudent()会加入到当前事务中执行。

Propagation_Mandatory

@Transactional(propagation = Propagation.MANDATORY, rollbackFor = Exception.class)
public void insertStudent(StudentDo studentDo) {studentMapper.insertStudent(studentDo);System.out.println("----------------------->Student插入成功!");
}@Test
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void insertSupportsTest() {studentService.insertStudent(studentDo);
}

Propagation_Mandatory

解释结果:MANDATORY 表示被修饰的方法必须在事务中运行。当单独调用 insertStudent 时,因为当前没有一个活动的事务,则会抛出异常。

throw new IllegalTransactionStateException(“Transaction propagation ‘mandatory’ but no existing transaction found”);

当调用 insertSupportsTest 时,insertStudent 则加入到 insertSupportsTest 的事务中,事务地执行。

Propagation_Required_New

表示被修饰的方法必须运行在它自己的事务中。一个新的事务会被启动。如果调用者存在当前事务,则在该方法执行期间,当前事务会被挂起。

private final static StudentDo studentDo = new StudentDo();
private final static ClassDo classDo = new ClassDo();
static {studentDo.setClassId(2);studentDo.setStudentName("requireNew");studentDo.setAddress("requireNew");
}
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
public void insertStudent(StudentDo studentDo) {studentMapper.insertStudent(studentDo);System.out.println("----------------------->Student插入成功!");
}
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
public void insertClassByException(ClassDo classDo) throws CustomException {classMapper.insertClass(classDo);throw new CustomException();
}@Test
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void insertInnerExceptionTest() {studentService.insertStudent(studentDo);try {classService.insertClassByException(classDo);} catch (CustomException e) {e.printStackTrace();}
}

结果解析:insertStudent(),insertClassByException() 方法执行时,外部方法事务被挂起,内部方法会新建事务,直至该方法执行结束,恢复外部方法事务执行。两者之间事务存在隔离性,insertClassByException() 方法遇到异常,触发事务回滚机制,但 insertStudent() 执行结果并受到影响。

如图所示:

触发事务回滚机制

Propagation_Required_New

Propagation_Not_Supported

表示被修饰的方法不应该运行在事务中。如果调用者存在当前事务,则该方法运行期间,当前事务将被挂起。

private final static ClassDo classDo = new ClassDo();
static {classDo.setClassName("notSupport");classDo.setClassNo("notSupport");
}
@Transactional(propagation = Propagation.NOT_SUPPORTED, rollbackFor = Exception.class)
public void insertClassByException(ClassDo classDo) throws CustomException {classMapper.insertClass(classDo);throw new CustomException();
}
@Test
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void insertInnerExceptionTest() {try {classService.insertClassByException(classDo);} catch (CustomException e) {e.printStackTrace();}
}

结果解释:即使外部方法开启事务,但是 insertClassByException() 执行,当前事务会挂起,not_support 以非事务方式运行,所以即使遇到异常情况,执行结果也不会触发回滚。

Propagation_Not_Supported

Propagation_Never

表示被修饰的方法不应该运行事务上下文中。如果调用者或者该方法中存在一个事务正在运行,则会抛出异常。

@Transactional(propagation = Propagation.NEVER, rollbackFor = Exception.class)
public void insertStudent(StudentDo studentDo) {studentMapper.insertStudent(studentDo);System.out.println("----------------------->Student插入成功!");
}@Test
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void insertInnerExceptionTest() {studentService.insertStudent(studentDo);
}

结果如图:

Propagation_Nested

  • 表示当前方法已经存在一个事务,那么该方法将会在嵌套事务中运行。

  • 嵌套的事务可以独立与当前事务进行单独地提交或者回滚。

  • 如果当前事务不存在,那么其行为与 Propagation_Required 一样。

  • 嵌套事务的概念就是内层事务依赖于外层事务。外层事务失败时,会回滚内层事务所做的动作。而内层事务操作失败并不会引起外层事务的回滚。

「1.外部未开启事务时,内部方法则新建事务执行」

private final static StudentDo studentDo = new StudentDo();
private final static ClassDo classDo = new ClassDo();
static {studentDo.setClassId(2);studentDo.setStudentName("NESTED");studentDo.setAddress("NESTED");classDo.setClassName("NESTED");classDo.setClassNo("NESTED");
}
@Test
public void insertTest() {studentService.insertStudent(studentDo);classService.insertClass(classDo);throw new RuntimeException();}
@Transactional(propagation = Propagation.NESTED, rollbackFor = Exception.class)
public void insertStudent(StudentDo studentDo) {studentMapper.insertStudent(studentDo);System.out.println("----------------------->Student插入成功!");
}
@Transactional(propagation = Propagation.NESTED, rollbackFor = Exception.class)
public void insertClass(ClassDo classDo) {classMapper.insertClass(classDo);System.out.println("----------------------->Class插入成功!");
}

结果:

Propagation_Nested

Propagation_Nested

「2.外部方法开启事务:」

  • 如果外部方法发生异常,则内部事务一起发生回滚操作;

  • 如果外部无异常情况,内部被调用方法存在异常情况,则内部方法独立回滚

//单测代码
private final static StudentDo studentDo = new StudentDo();
private final static ClassDo classDo = new ClassDo();
static {studentDo.setClassId(2);studentDo.setStudentName("NESTED_InnerException");studentDo.setAddress("NESTED_InnerException");classDo.setClassName("NESTED_InnerException");classDo.setClassNo("NESTED_InnerException");
}
@Test
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void insertInnerExceptionThrowsTest() throws CustomException {studentMapper.insertStudent(studentDo);classService.insertClassByException(classDo);
}
//NESTED事务传播行为
@Transactional(propagation = Propagation.NESTED, rollbackFor = Exception.class)
public void insertClassByException(ClassDo classDo) throws CustomException {classMapper.insertClass(classDo);throw new RuntimeException();
}

相关内容

热门资讯

在大吉订购APP订购交易亏损被...   近期,不少投资者反映在“大吉订购App”上投资白银、铂金等商品遭遇严重亏损。该平台宣称提供现货订...
铂银壹号APP商品订购交易亏损...   许多中小投资者误以为网上的“商品现货订购”是合规合法的理财渠道,殊不知这背后隐藏着巨大的合规风险...
兴鑫淘金APP贵金属订购交易亏...   白银、贵金属或其他商品类投资,除国家批准的正规期货交易场所及持牌期货公司外,市面上大量第三方交易...
铂银掘金APP无资质的违规平台...   铂银掘金等平台的投资者,往往是被“注册送现金”“充值返现券”“首单免亏”等广告话术吸引,进而下载...
八方淘金APP订购交易不合规,...   “八方淘金APP”“铂银掘金APP”“铂银壹号APP”等软件被指以“现货订购”为名,实际采用高杠...
在指尖订购APP订购商品亏损被...   指尖订购APP这类平台,表面是“订购交易”,实则是披着电商外衣的非法期货陷阱。绝大多数投资者被蒙...
在犀贝易购APP做商品订购交易...   市面上打着“现货投资”“贵金属交易”旗号的APP并不少,但真正合规、受监管的平台并不多。犀贝易购...
易达淘金APP虚假商品订购交易...   易达淘金APP存在严重的虚假宣传与违规交易行为。该平台打着“商品订购”的旗号,实则通过不合规的交...
大浪淘金APP白银订购亏损被骗...   大浪淘金APP最隐蔽的套路,在于其完全封闭的内盘交易系统。表面上,它宣称与国际贵金属市场接轨;实...
一品好购APP上订购白银出现亏...   一品好购APP原名“一品订购”,运营方为上海宝智未来信息技术有限公司。近期不少投资者反映在该平台...
九州订购APP贵金属订购投资骗...   九州订购APP正通过抖.音、快手、今日头条等平台大量投放投资广告,用“5元起投”“充值返现”“0...
坚锅淘金APP交易软件并不正规...   当前,市面上大量所谓的“现货商城订购”交易软件,早已偏离了正常的实物商品买卖,演变为高杠杆、高手...
斗金订购APP贵金属期货交易骗...   斗金订购APP的投资者往往被铺天盖地的广告宣传所诱导——注册即送现金、充值返现券等看似诱人的福利...
警惕!云启购APP违规经营,披...   "5元起投、充值返现、稳赚不赔"——这些诱人的广告语背后,可能是一个精心设计的金融陷阱。  近期...
量子淘金APP贵金属投资不合规...   "4元起投""充值返券""月入过万"——这些极具诱惑力的广告语在短视频平台、社交软件上铺天盖地。...
鑫汇智投APP贵金属商品订购骗...   鑫汇智投APP非法期货投资骗局,受害者在投资过程中根本不懂交易,也不去研究和验证这个平台是否正规...
矅光智策APP平台违规经营非法...   矅光智策APP对外宣称是“商城订购”“现货交易”,但查询其经营范围,并未显示具备开展现货期货交易...
黄金珠宝怎么选?内侧刻字都代表... 潮新闻客户端 记者 马焱 通讯员 任倩影 9月8日下午,“云游杭州 乐购金秋”——“浙城·浙放心”活...
原创 冰... 最近全球国债市场正在上演魔幻一幕: 美债、日债、欧债遭遇猛烈抛售,收益率冲到几十年新高,美国财政部都...
日韩股市开盘表现分化 上证报中国证券网讯 9月9日,日韩股市开盘表现分化,韩国综合指数开盘上涨0.26%,报6972.87...