当前位置: 首页 > news >正文

Spring @Transactional事务传播行为详解

 

目录

一、无事务情况

二、有事务情况

REQUIRED

SUPPORTS

MANDATORY

REQUIRES_NEW

NOT_SUPPORTED

NEVER

NESTED


Spring的事务传播机制用于控制在多个事务方法相互调用时事务的行为。

在复杂的业务场景中,多个事务方法之间的调用可能会导致事务的一致性,如出现数据丢失、重复提交等问题,使用事务传播机制可以避免这些问题的发生,保证事务的一致性和完整性。

Spring的事务规定了7种事务的传播级别,默认的传播机制是REQUIRED

一、无事务情况

我们先看一段代码:

public void testPropagationTrans() {stuService.saveParent();stuService.saveChildren();
}public void saveParent() {Stu stu = new Stu();stu.setName("parent");stu.setAge(30);stuMapper.insert(stu);
}public void saveChildren() {saveChild1();int a = 1 / 0;saveChild2();
}public void saveChild1() {Stu stu1 = new Stu();stu1.setName("child-1");stu1.setAge(11);stuMapper.insert(stu1);
}public void saveChild2() {Stu stu2 = new Stu();stu2.setName("child-2");stu2.setAge(22);stuMapper.insert(stu2);
}

当我们运行testPropagationTrans方法,发现数据库是这样的

当saveChildren方法中有异常,也不会做事务回滚,其实也正常,因为我们也没加事务。

二、有事务情况

注:我们为了好理解,把testPropagationTrans叫做父方法,saveParent和saveChildren叫做子方法。

REQUIRED

REQUIRED:如果不存在事务则开启一个事务,如果存在事务则加入之前的事务,总是只有一个事务在执行。

举例:领导没饭吃,我有钱,我会自己买了自己吃;领导有的吃,会分给你一起吃。

比如我们代码如下;

public void testPropagationTrans() {stuService.saveParent();stuService.saveChildren();
}public void saveParent() {Stu stu = new Stu();stu.setName("parent");stu.setAge(30);stuMapper.insert(stu);
}@Transactional(propagation = Propagation.REQUIRED)
public void saveChildren() {saveChild1();int a = 1 / 0;saveChild2();
}public void saveChild1() {Stu stu1 = new Stu();stu1.setName("child-1");stu1.setAge(11);stuMapper.insert(stu1);
}public void saveChild2() {Stu stu2 = new Stu();stu2.setName("child-2");stu2.setAge(22);stuMapper.insert(stu2);
}

这样的结果很好理解,我在子方法有事务,当然只管我自己的回滚,并不管其它的,正所谓我有钱我自己买饭吃,别人我不管。

但是当你把@Transactional(propagation = Propagation.REQUIRED)放在父方法上,那么数据库里将会什么数据都没有,因为父方法有事务,子方法会加入此事务,相当于都包在里面了,所以有异常则全部回滚。

SUPPORTS

SUPPORTS:有事务则加入事务,没有事务则普通执行。

举例:领导没饭吃,我也没饭吃;领导有饭吃,我也有饭吃。

public void testPropagationTrans() {stuService.saveParent();stuService.saveChildren();
}public void saveParent() {Stu stu = new Stu();stu.setName("parent");stu.setAge(30);stuMapper.insert(stu);
}@Transactional(propagation = Propagation.SUPPORTS)
public void saveChildren() {saveChild1();int a = 1 / 0;saveChild2();
}public void saveChild1() {Stu stu1 = new Stu();stu1.setName("child-1");stu1.setAge(11);stuMapper.insert(stu1);
}public void saveChild2() {Stu stu2 = new Stu();stu2.setName("child-2");stu2.setAge(22);stuMapper.insert(stu2);
}

很明显,如果父方法没有事务,那就相当于都没事务,就正常运行了。

但如果父方法有@Transactional(propagation = Propagation.REQUIRED)注解,则加入父方法事务。

MANDATORY

MANDATORY:强制有事务,没有事务则报异常。

举例:领导必须管饭,不管饭我就抛出异常。

public void testPropagationTrans() {stuService.saveParent();stuService.saveChildren();
}public void saveParent() {Stu stu = new Stu();stu.setName("parent");stu.setAge(30);stuMapper.insert(stu);
}@Transactional(propagation = Propagation.MANDATORY)
public void saveChildren() {saveChild1();int a = 1 / 0;saveChild2();
}public void saveChild1() {Stu stu1 = new Stu();stu1.setName("child-1");stu1.setAge(11);stuMapper.insert(stu1);
}public void saveChild2() {Stu stu2 = new Stu();stu2.setName("child-2");stu2.setAge(22);stuMapper.insert(stu2);
}

这样系统直接抛出异常,因为父方法没有事务。

REQUIRES_NEW

REQUIRES_NEW:每次执行新开一个事务,如果当前存在事务,则把当前事务挂起。

举例:领导管饭,但我偏不要,我自己买了自己吃,如果领导不管饭,我自己也有钱买饭。

public void testPropagationTrans() {stuService.saveParent();stuService.saveChildren();
}public void saveParent() {Stu stu = new Stu();stu.setName("parent");stu.setAge(30);stuMapper.insert(stu);
}@Transactional(propagation = Propagation.REQUIRES_NEW)
public void saveChildren() {saveChild1();int a = 1 / 0;saveChild2();
}public void saveChild1() {Stu stu1 = new Stu();stu1.setName("child-1");stu1.setAge(11);stuMapper.insert(stu1);
}public void saveChild2() {Stu stu2 = new Stu();stu2.setName("child-2");stu2.setAge(22);stuMapper.insert(stu2);
}

结果说明,子方法自己创建了一个新的事务自己用。

如果我父方法上加了事务,这时数据库会发现什么数据都没有。

但我们再考虑一种情况,代码如下:

@Transactional(propagation = Propagation.REQUIRES)
public void testPropagationTrans() {stuService.saveParent();stuService.saveChildren();int a = 1 / 0;
}public void saveParent() {Stu stu = new Stu();stu.setName("parent");stu.setAge(30);stuMapper.insert(stu);
}@Transactional(propagation = Propagation.REQUIRES_NEW)
public void saveChildren() {saveChild1();saveChild2();
}public void saveChild1() {Stu stu1 = new Stu();stu1.setName("child-1");stu1.setAge(11);stuMapper.insert(stu1);
}public void saveChild2() {Stu stu2 = new Stu();stu2.setName("child-2");stu2.setAge(22);stuMapper.insert(stu2);
}

我们把异常代码放在父方法中,这时数据库结果发现:

这个结果是如何造成的呢?

其实当执行代码时,子方法创建了一个新的事务,并把父方法的事务挂起,所以也就是说,子事务是子事务,父事务是父事务,这是两个事务。

所以当父方法有异常时,直接回滚saveParent方法,并不影响子事务的操作。

NOT_SUPPORTED

NOT_SUPPORTED:如果当前有事务,则把事务挂起,自己不使用事务去运行数据库操作。

举例:领导管饭,分一点给你,但你太忙了,放一边不吃。

public void testPropagationTrans() {stuService.saveParent();stuService.saveChildren();
}public void saveParent() {Stu stu = new Stu();stu.setName("parent");stu.setAge(30);stuMapper.insert(stu);
}@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void saveChildren() {saveChild1();int a = 1 / 0;saveChild2();
}public void saveChild1() {Stu stu1 = new Stu();stu1.setName("child-1");stu1.setAge(11);stuMapper.insert(stu1);
}public void saveChild2() {Stu stu2 = new Stu();stu2.setName("child-2");stu2.setAge(22);stuMapper.insert(stu2);
}

看的出来,压根没使用事务。

当我在父方法上加上事务。

@Transactional(propagation = Propagation.REQUIRES)
public void testPropagationTrans() {stuService.saveParent();stuService.saveChildren();
}public void saveParent() {Stu stu = new Stu();stu.setName("parent");stu.setAge(30);stuMapper.insert(stu);
}@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void saveChildren() {saveChild1();int a = 1 / 0;saveChild2();
}public void saveChild1() {Stu stu1 = new Stu();stu1.setName("child-1");stu1.setAge(11);stuMapper.insert(stu1);
}public void saveChild2() {Stu stu2 = new Stu();stu2.setName("child-2");stu2.setAge(22);stuMapper.insert(stu2);
}

这个结果说明子方法没有事务,而且还把父事务挂起,所以父有事务,导致saveParent方法可以回滚。

NEVER

NEVER:如果当前有事务存在,则抛出异常。

举例:领导管饭,我不想吃,我抛出异常。

@Transactional(propagation = Propagation.REQUIRES)
public void testPropagationTrans() {stuService.saveParent();stuService.saveChildren();
}public void saveParent() {Stu stu = new Stu();stu.setName("parent");stu.setAge(30);stuMapper.insert(stu);
}@Transactional(propagation = Propagation.NEVER)
public void saveChildren() {saveChild1();int a = 1 / 0;saveChild2();
}public void saveChild1() {Stu stu1 = new Stu();stu1.setName("child-1");stu1.setAge(11);stuMapper.insert(stu1);
}public void saveChild2() {Stu stu2 = new Stu();stu2.setName("child-2");stu2.setAge(22);stuMapper.insert(stu2);
}

代码直接报错异常,因为never不允许你(父)有事务。

NESTED

NESTED:如果当前有事务,则开启子事务(嵌套事务),嵌套事务是独立提交或者回滚;如果当前没有事务,则同REQUIRED。但是如果主事务提交,则会携带子事务一起提交。

如果主事务回滚,则子事务一起回滚,相反,子事务异常,则父事务可以回滚或不回滚。

举例:领导决策不对,老板怪罪,领导带着小弟一同受罪。小弟出了差错,领导可以推卸责任,也可以承担责任。

@Transactional(propagation = Propagation.REQUIRES)
public void testPropagationTrans() {stuService.saveParent();stuService.saveChildren();int a = 1 / 0;
}public void saveParent() {Stu stu = new Stu();stu.setName("parent");stu.setAge(30);stuMapper.insert(stu);
}@Transactional(propagation = Propagation.NESTED)
public void saveChildren() {saveChild1();saveChild2();
}public void saveChild1() {Stu stu1 = new Stu();stu1.setName("child-1");stu1.setAge(11);stuMapper.insert(stu1);
}public void saveChild2() {Stu stu2 = new Stu();stu2.setName("child-2");stu2.setAge(22);stuMapper.insert(stu2);
}

结果数据库什么数据都没有。因为父有事务,当父方法有异常,里面的嵌套事务要被回滚。

但如果子事务发生异常呢?

@Transactional(propagation = Propagation.REQUIRES)
public void testPropagationTrans() {stuService.saveParent();stuService.saveChildren();
}public void saveParent() {Stu stu = new Stu();stu.setName("parent");stu.setAge(30);stuMapper.insert(stu);
}@Transactional(propagation = Propagation.NESTED)
public void saveChildren() {saveChild1();int a = 1 / 0;saveChild2();
}public void saveChild1() {Stu stu1 = new Stu();stu1.setName("child-1");stu1.setAge(11);stuMapper.insert(stu1);
}public void saveChild2() {Stu stu2 = new Stu();stu2.setName("child-2");stu2.setAge(22);stuMapper.insert(stu2);
}

结果数据库还是什么数据都没有,这是因为子事务的异常会影响到父方法。但这个是可选的,我们可以给父方法调用加异常,这样就不会影响父方法事务了,如下:

@Transactional(propagation = Propagation.REQUIRES)
public void testPropagationTrans() {stuService.saveParent();try {stuService.saveChildren();}catch (Exception e) {e.printStackTrace();}
}

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • Datawhale AI 夏令营第四期 大模型技术-微调 task3 数据增强与评分
  • 汽车4S店管理系统-计算机毕设Java|springboot实战项目
  • 【Linux基础】对Linux权限的理解与管理
  • 解决方案上新了丨趋动科技推出基于银河麒麟操作系统的异构算力池化解决方案
  • 统一响应结果封装,Result类的实现【后端 06】
  • 鸿蒙开发APP应用UX体验标准
  • 【应用】 Flask 和 WebSockets 开发实时聊天应用程序
  • 【论文笔记】:YOLOv8-QSD 自动驾驶场景小目标检测算法
  • skywalking架构
  • Qt QPushButton::clicked和QPushButton::click的区别
  • 平安城市/雪亮工程现状及需求分析:EasyCVR视频汇聚平台助力雪亮工程项目建设
  • 短信软件如何高效处理空号问题
  • Python 实现行为驱动开发 (BDD) 自动化测试详解
  • 代码随想录算法训练营第四十三天 | 动态规划 part10
  • CentOS7下载与安装 即配置网卡
  • Apache Zeppelin在Apache Trafodion上的可视化
  • css布局,左右固定中间自适应实现
  • es的写入过程
  • HTML-表单
  • httpie使用详解
  • laravel 用artisan创建自己的模板
  • SpiderData 2019年2月23日 DApp数据排行榜
  • SQLServer插入数据
  • yii2中session跨域名的问题
  • 给github项目添加CI badge
  • 给新手的新浪微博 SDK 集成教程【一】
  • 关于 Linux 进程的 UID、EUID、GID 和 EGID
  • 浏览器缓存机制分析
  • 免费小说阅读小程序
  • 使用 5W1H 写出高可读的 Git Commit Message
  • 微信小程序填坑清单
  • 我建了一个叫Hello World的项目
  • 译有关态射的一切
  • 最简单的无缝轮播
  • 阿里云重庆大学大数据训练营落地分享
  • 测评:对于写作的人来说,Markdown是你最好的朋友 ...
  • 没有任何编程基础可以直接学习python语言吗?学会后能够做什么? ...
  • ​LeetCode解法汇总1410. HTML 实体解析器
  • ​LeetCode解法汇总2182. 构造限制重复的字符串
  • # 手柄编程_北通阿修罗3动手评:一款兼具功能、操控性的电竞手柄
  • #AngularJS#$sce.trustAsResourceUrl
  • #gStore-weekly | gStore最新版本1.0之三角形计数函数的使用
  • $.ajax()方法详解
  • (2021|NIPS,扩散,无条件分数估计,条件分数估计)无分类器引导扩散
  • (a /b)*c的值
  • (java版)排序算法----【冒泡,选择,插入,希尔,快速排序,归并排序,基数排序】超详细~~
  • (Java实习生)每日10道面试题打卡——JavaWeb篇
  • (二)换源+apt-get基础配置+搜狗拼音
  • (附源码)计算机毕业设计ssm基于B_S的汽车售后服务管理系统
  • (附源码)计算机毕业设计SSM疫情下的学生出入管理系统
  • (篇九)MySQL常用内置函数
  • (三分钟了解debug)SLAM研究方向-Debug总结
  • (十六)视图变换 正交投影 透视投影
  • (四)c52学习之旅-流水LED灯
  • (转)甲方乙方——赵民谈找工作