Spring事务及其传播机制(二)

目录

1.@Transcational详解

1.1 rollbackFor

2事务隔离级别

2.1MySQL事务隔离级别回顾

2.2Spring事务隔离级别

3.Spring事务传播机制

3.1什么是事务传播机制

3.2事务的传播机制有哪些

3.3 Spring 事务传播机制使用和各种场景演示

3.3.1 REQUIRED(加入事务)

3.3.2 REQUIRES_NEW(新建事务)

3.3.3 NEVER(不支持当前事务,抛异常)

3.3.4 NESTED(嵌套事务)

3.3.5NESTED和REQUIRED 有什么区别?

4.总结


承接上文Spring事务及其传播机制(一)

1.@Transcational详解

通过之前的代码,我们学习了@Transactional的基本使用.接下来我们学习@Transactional注解的使用细节.
我们主要学习@Transactional注解当中的三个常见属性:
1.rollbackFor:异常回滚属性.指定能够触发事务回滚的异常类型.可以指定多个异常类型
2.Isolation:事务的隔离级别.默认值为Isolation.DEFAULT
3.propagation:事务的传播机制.默认值为Propagation.REQUIRED

1.1 rollbackFor

@Transactional默认只在遇到运行时异常和Error时才会回滚,非运行时异常不回滚.即Exception的子类中,除了RuntimeException及其子类.

我们上面为了演示事务回滚, ⼿动设置了程序异常

int a = 10/0;

接下来我们把异常改为如下代码

@Transactional@RequestMapping("/r2")public String r2(String name,String password) throws IOException { //⽤户注册userService.registryUser(name,password);log.info("⽤户数据插⼊成功");if (true){throw new IOException();}return "r2";} 

运行程序:

发现虽然程序抛出了异常, 但是事务依然进行了提交. 

如果我们需要所有异常都回滚,需要来配置@Transactional注解当中的rollbackFor属性,通过rollbackFor这个属性指定出现何种异常类型时事务进行回滚.

@Transactional(rollbackFor = Exception.class) @RequestMapping("/r2")public String r2(String name,String password) throws IOException { //⽤户注册userService.registryUser(name,password);log.info("⽤户数据插⼊成功");if (true){throw new IOException();}return "r2";}

此次运行程序抛出异常后事务将会进行回滚,IOException为Exception的子类

结论:

  Spring的事务管理中 ,默认只在遇到运⾏时异常RuntimeExceptionError时才会回滚.

  如果需要回滚指定类型的异常, 可以通过rollbackFor属性来指定.

2事务隔离级别

2.1MySQL事务隔离级别回顾

SQL 标准定义了四种隔离级别, MySQL 全都支持. 这四种隔离级别分别:

1.读未提交(READ UNCOMMITTED):读未提交,也叫未提交读.该隔离级别的事务可以看到其他事务中未提交的数据

因为其他事务未提交的数据可能会发生回滚,但是该隔离级别却可以读到,我们把该级别读到的数据称之为脏数据,这个问题称之为脏读.

2.读提交(READ COMMITTED):读已提交,也叫提交读.该隔离级别的事务能读取到已经提交事务的数据

该隔离级别不会有脏读的问题.但由于在事务的执行中可以读取到其他事务提交的结果,所以在不同时间的相同SQL查询可能会得到不同的结果,这种现象叫做不可重复读

3.可重复读(REPEATABLE READ):事务不会读到其他事务对已有数据的修改,即使其他事务已提交.也就可以确保同一事务多次查询的结果一致,但是其他事务新插入的数据,是可以感知到的.这也就引发了幻读问题,可重复读,是MySQL的默认事务隔离级别

比如此级别的事务正在执行时,另一个事务成功的插入了某条数据,但因为它每次查询的结果都是一样的,所以会导致查询不到这条数据,自己重复插入时又失败(因为唯一约束的原因).明明在事务中查询不到这条信息,但自己就是插入不进去,这个现象叫幻读.

4.串行化(SERIALIZABLE):序列化,事务最高隔离级别.它会强制事务排序,使之不会发生冲突,从而解决了脏读,不可重复读和幻读问题,但因为执行效率低,所以真正使用的场景并不多

2.2Spring事务隔离级别

Spring中事务隔离级别有5种:

1.Isolation.DEFAULT:以连接的数据库的事务隔离级别为主.

2.Isolation.READ_UNCOMMITTED:读未提交,对应SQL标准中READ UNCOMMITTED

3.Isolation.READ_COMMITTED:读已提交,对应SQL标准中READ COMMITTED

4.Isolation.REPEATABLE_READ:可重复读,对应SQL标准中REPEATABLE READ

5.Isolation.SERIALIZABLE:串行化,对应SQL标准中SERIALIZABLE

 public enum Isolation {DEFAULT(-1),READ_UNCOMMITTED(1),READ_COMMITTED(2),REPEATABLE_READ(4),SERIALIZABLE(8); private final int value; private Isolation(int value) {this.value = value;}public int value() {return this.value;}}

Spring中事务隔离级别可以通过@Transactional中的isolation属性进行设置

 @Transactional(isolation = Isolation.READ_COMMITTED)@RequestMapping("/r3")public String r3(String name,String password) throws IOException { //... 代码省略return "r3"; }

3.Spring事务传播机制

3.1什么是事务传播机制

事务传播机制就是:多个事务方法存在调用关系时,事务是如何在这些方法间进行传播的.

比如有两个方法A,B都被@Transactional修饰,A方法调用B方法
A方法运行时,会开启一个事务.当A调用B时,B方法本身也有事务,此时B方法运行时,是加入A的事务,是创建一个新的事务呢?
这个就涉及到了事务的传播机制。

比如公司流程管理执行任务之前,需要先写执行文档,任务执行结束,再写总结汇报

此时A部门有一项工作,需要B部门的支援,此时B部门是直接使用A部门的文档,还是新建一个文档呢?

事务隔离级别解决的是多个事务同时调用一个数据库的问题

而事务传播机制解决的是一个事务在多个节点(方法)中传递的问题

3.2事务的传播机制有哪些

@Transactional注解支持事务传播机制的设置,通过propagation属性来指定传播行为.

Spring事务传播机制有以下7种:

1.Propagation.REQUIRED:默认的事务传播级别.如果当前存在事务,则加入该事务.如果当前没有事务,则创建一个新的事务.

2.Propagation.SUPPORTS:如果当前存在事务,则加入该事务.如果当前没有事务,则以非事务的方式继续运行.

3.Propagation.MANDATORY :强制性.如果当前存在事务,则加入该事务.如果当前没有事务,则抛出异常.

4.Propagation.REQUIRES_NEW:创建一个新的事务,如果当前存在事务,则把当前事务挂起.也就是说不管外部方法是否开启事务,Propagation.REQUIRES_NEW修饰的内部方法都会新开启自己的事务,且开启的事务相互独立,互不干扰.

5.Propagation.NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起(不用).

6.Propagation.NEVER:以非事务方式运行,如果当前存在事务,则抛出异常

7.Propagation.NESTED:如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行.如果当前没有事务,则该取值等价于PROPAGATION_REQUIRED.

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

3.3 Spring 事务传播机制使用和各种场景演示

对于以上事务传播机制,我们重点关注以下两个就可以了:

1.REQUIRED(默认值)

2.REQUIRES_NEW

3.3.1 REQUIRED(加入事务)

看下面代码实现:

1.用户注册,插入一条数据

2.记录操作日志,插入一条数据(出现异常)

观察propagation =Propagation.REQUIRED的执行结果

 @RequestMapping("/propaga") @RestControllerpublic class PropagationController { @Autowiredprivate UserService userService; @Autowiredprivate LogService logService;@Transactional(propagation = Propagation.REQUIRED)@RequestMapping("/p1")public String r3(String name,String password){ //⽤户注册userService.registryUser(name,password); //记录操作⽇志logService.insertLog(name,"⽤户注册");return "r3"; }}

对应的UserService和LogService都添加@Transactional(propagation =Propagation.REQUIRED)

 @Slf4j@Servicepublic class UserService { @Autowiredprivate UserInfoMapper userInfoMapper;@Transactional(propagation = Propagation.REQUIRED)public void registryUser(String name,String password){//插⼊⽤户信息userInfoMapper.insert(name,password);}}
 @Slf4j@Servicepublic class LogService { @Autowiredprivate LogInfoMapper logInfoMapper; @Transactional(propagation = Propagation.REQUIRED)public void insertLog(String name,String op){int a=10/0;  //记录⽤户操作logInfoMapper.insertLog(name,"⽤户注册"); }}

运行程序,发现数据库没有插入任何数据流程描述:

1.p1方法开始事务

2.用户注册,插入一条数据(执行成功)(和p1使用同一个事务)

3.记录操作日志,插入一条数据(出现异常,执行失败)(和p1使用同一个事务)

4.因为步骤3出现异常,事务回滚.步骤2和3使用同一个事务,所以步骤2的数据也回滚了.

3.3.2 REQUIRES_NEW(新建事务)

将上述UserService和LogService中相关方法事务传播机制改为Propagation.REQUIRES_NEW

@Servicepublic class UserService { @Autowiredprivate UserInfoMapper userInfoMapper; @Transactional(propagation = Propagation.REQUIRES_NEW)public void registryUser(String name,String password){//插⼊⽤⼾信息userInfoMapper.insert(name,password);}}
 @Servicepublic class LogService { @Autowiredprivate LogInfoMapper logInfoMapper; @Transactional(propagation = Propagation.REQUIRES_NEW)public void insertLog(String name,String op){int a=10/0;  //记录⽤⼾操作logInfoMapper.insertLog(name,"⽤户注册"); }}

运行程序,发现用户数据插入成功了,日志表数据插入失败.LogService方法中的事务不影响UserService中的事务.当我们不希望事务之间相互影响时,可以使用该传播行为.

3.3.3 NEVER(不支持当前事务,抛异常)

修改UserService中对应方法的事务传播机制为Propagation.NEVER

 @Slf4j@Servicepublic class UserService { @Autowiredprivate UserInfoMapper userInfoMapper; @Transactional(propagation = Propagation.NEVER)public void registryUser(String name,String password){//插⼊⽤⼾信息userInfoMapper.insert(name,password);}}

程序执行报错, 没有数据插入

3.3.4 NESTED(嵌套事务)

将上述UserService和LogService中相关方法事务传播机制改为Propagation.NESTED

 @Slf4j@Servicepublic class UserService { @Autowiredprivate UserInfoMapper userInfoMapper; @Transactional(propagation = Propagation.NESTED)public void registryUser(String name,String password){//插⼊⽤⼾信息userInfoMapper.insert(name,password);}}
 @Servicepublic class LogService { @Autowiredprivate LogInfoMapper logInfoMapper; @Transactional(propagation = Propagation.NESTED)public void insertLog(String name,String op){int a=10/0;  //记录⽤⼾操作logInfoMapper.insertLog(name,"⽤户注册"); }}

运行程序, 发现没有任何数据插入 . 

流程描述:

1.Controller中p1方法开始事务

2.UserService用户注册,插入一条数据(嵌套p1事务)

3.LogService记录操作日志,插入一条数据(出现异常,执行失败)(嵌套p1事务,回滚当前事务,数
据添加失败)

4.由于是嵌套事务,LogService出现异常之后,往上找调用它的方法和事务,所以用户注册也失败了.

5.最终结果是两个数据都没有添加

p1事务可以认为是父事务,嵌套事务是子事务.父事务出现异常,子事务也会回滚,子事务出现异常,如果不进行处理,也会导致父事务回滚。

3.3.5NESTED和REQUIRED 有什么区别?

我们在LogService进行当前事务回滚,修改LogService代码如下:

 @Servicepublic class LogService { @Autowiredprivate LogInfoMapper logInfoMapper; @Transactional(propagation = Propagation.REQUIRES_NEW)public void insertLog(String name,String op){try{int a=10/0;  }catch(Exception e){        // ⼿动回滚事务TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); }//记录⽤⼾操作logInfoMapper.insertLog(name,"⽤户注册"); }}

重新运行程序,发现用户表数据添加成功,日志表添加失败.
LogService中的事务已经回滚,但是嵌套事务不会回滚嵌套之前的事务,就是说嵌套事务可以实现部分事务回滚.REQUIRED如果回滚就是回滚所有事物,不能实现部分事务回滚(属于同一事务)

整个事务如果全部执行成功,二者的结果是一样的.
如果事务一部分执行成功,REQUIRED加入事务会导致整个事务全部回滚.NESTED嵌套事务可以实现局部回滚,不会影响上一个方法中执行的结果.
嵌套事务之所以能够实现部分事务的回滚,是因为事务中有一个保存点(savepoint)的概念,嵌套事务进入之后相当于新建了一个保存点,而滚回时只回滚到当前保存点.

4.总结

1.Spring中使用事务,有两种方式:编程式事务(手动操作)和声明式事务.其中声明式事务使用较多,在方法上添加@Transactional就可以实现了

2.通过@Transactional(isolation =Isolation.SERIALIZABLE)设置事务的隔离级别.Spring中的事务隔离级别有5种

3.通过@Transactional(propagation =Propagation.REQUIRED)设置事务的传播机制,Spring中的事务传播级别有7种,重点关注REQUIRED(默认值)和REQUIRES_NEW

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/38339.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Apache ZooKeeper 简介

介绍 Apache ZooKeeper 是一种分布式协调服务,旨在管理和同步大量分布式应用程序。ZooKeeper 是 Apache 软件基金会下的一个开源项目,它解决了维护分布式应用程序的配置信息、命名、分布式同步和组服务的复杂性。本文探讨了 ZooKeeper 的架构、功能、应…

商标字体的选择:企业和个人申请注册商标攻略!

对于汉字商标,就会涉及到字体的选择,普推商标老杨也经常看到企业因为文字商标字体侵权收到相关字体公司的律师函,所以商标字体选择上要特别注意。 建议选择可以商用的免费字体,常见的有黑体、宋体等,如果这些字体前面…

基于matlab的可乐标签模板匹配

1 建模思路 1.图像预处理: 如果目标图像和模板图像是彩色的(即RGB图像),则将它们转换为灰度图像,以便在单通道上进行匹配。使用rgb2gray函数进行灰度化。 2.获取模板大小: 使用size函数获取模板图像的高…

2000-2022年上市公司数字化转型与绿色创新质量匹配数据(含控制变量)

2000-2022年上市公司数字化转型与绿色创新质量匹配数据(含控制变量)https://download.csdn.net/download/a519573917/89501000 目录 上市公司数字化转型与绿色创新质量匹配的实证研究 一、引言 二、文献综述 三、实证模型 四、数据来源与描述性统计 …

tomcat8.5在windows下运行出现日志中文乱码

更多ruoyi-nbcio功能请看演示系统 gitee源代码地址 前后端代码: https://gitee.com/nbacheng/ruoyi-nbcio 演示地址:RuoYi-Nbcio后台管理系统 http://218.75.87.38:9666/ 更多nbcio-boot功能请看演示系统 gitee源代码地址 后端代码: h…

Linux 2-Vim使用

1 什么是vi及vim&#xff1f; vi是文本编辑器&#xff1b;vim是程序开发工具。 2 vi的几种模式 1 一般模式&#xff1a;vi <fileName> 就进入命令模式&#xff0c;可以删除或者复制粘贴 2 编辑模式&#xff1a;修改内容 3 命令行模式&#xff1a;最下面一行&#xf…

NetSuite Amount正负符号在Saved Search和DataSet中的不同含义

近期在一个项目中碰到Amount取值的Bug&#xff0c;原因是我们的代码中数据源从Saved Search转为了DataSet&#xff0c;由于这个转换导致了Amount的正负值混乱。今天记录一下。 正负号原则 • Saved Search&#xff0c; Amount的正负需要考虑科目类型。 Amount字段根据科目类型…

昇思25天学习打卡营第10天 | 基于MindNLP+MusicGen生成自己的个性化音乐

基于MindNLPMusicGen生成自己的个性化音乐 MusicGen是来自Meta AI的Jade Copet等人提出的基于单个语言模型&#xff08;LM&#xff09;的音乐生成模型&#xff0c;能够根据文本描述或音频提示生成高质量的音乐样本&#xff0c;相关研究成果参考论文《Simple and Controllable …

远程过程调用PRC

简介 远程过程调用&#xff08;Remote Procedure Call, RPC)&#xff0c;是一个计算机通信协议。该协议允许运行于一台计算机的程序调用另一个地址空间的子程序&#xff0c;且不需要考虑交互作用的细节。 RPC是一种服务器&#xff0c;客户端模式&#xff0c;是一个通过发送请…

【效率提升】新一代效率工具平台utools

下载地址&#xff1a;utools uTools这款软件&#xff0c;是一款功能强大且高度可定制的效率神器&#xff0c;使用快捷键alt space(空格) 随时调用&#xff0c;支持调用系统应用、用户安装应用和市场插件等。 utools可以调用系统设置和内置应用&#xff0c;这样可以方便快捷的…

KV260视觉AI套件--PYNQ-DPU

目录 1. 简介 2. DPU 原理介绍 2.1 基本原理 2.2 增强型用法 3. DPU 开发流程 3.1 添加 DPU IP 3.2 在 BD 中调用 3.3 配置 DPU 参数 3.4 DPU 与 Zynq MPSoC互联 3.5 分配地址 3.6 生成 Bitstream 3.7 生成 BOOT.BIN 4. 总结 1. 简介 在《Vitis AI 环境搭建 &…

深入理解TCP协议格式(WireShark分析)

传输控制协议&#xff08;TCP&#xff09;是互联网中最为关键的通信协议之一。了解TCP协议的细节不仅对于网络工程师至关重要&#xff0c;对于任何涉及网络通信的软件开发人员而言都是必备的知识。本文旨在深入探讨TCP协议&#xff0c;从协议的基本概述到其工作机制&#xff0c…

【sqlite3】联系人管理系统

SQLite3实现简单的联系人管理系统 有关sqlite3的基础知识请点击&#xff1a;SQLite3的使用 效果展示&#xff1a; 创建一个名为contacts.db的数据库 首先&#xff0c;我们需要创建一个名为contacts.db的数据库&#xff0c;并建立一个名为"contact"的表&#xff0…

一篇文章理解堆栈溢出

一篇文章理解堆栈溢出 引言栈溢出ret2text答案 ret2shellcode答案 ret2syscall答案 栈迁移答案 堆溢出 unlink - UAF堆结构小提示 向前合并/向后合并堆溢出题答案 引言 让新手快速理解堆栈溢出&#xff0c;尽可能写的简单一些。 栈溢出 代码执行到进入函数之前都会记录返回地…

Android 10.0 关于定制自适应AdaptiveIconDrawable类型的动态日历图标的功能实现系列一

1.前言 在10.0的系统rom定制化开发中,在关于定制动态时钟图标中,原系统是不支持动态日历图标的功能,所以就需要从新 定制动态时钟图标关于自适应AdaptiveIconDrawable类型的样式,就是可以支持当改变系统图标样式变化时,动态日历 图标的背景图形也跟着改变,所以接下来就来…

BGE M3-Embedding 模型介绍

BGE M3-Embedding来自BAAI和中国科学技术大学&#xff0c;是BAAI开源的模型。相关论文在https://arxiv.org/abs/2402.03216&#xff0c;论文提出了一种新的embedding模型&#xff0c;称为M3-Embedding&#xff0c;它在多语言性&#xff08;Multi-Linguality&#xff09;、多功能…

19 解决问题的策略

众所周知&#xff0c;每个学期都会有一个单元“解决问题的策略”。而在此文章&#xff0c;我们会把小学阶段所有策略都进行一一讲解。 每个年级的两个学期中的策略&#xff0c;其实有异曲同工之处。 三年级&#xff1a;从条件和问题出发解决问题四年级&#xff1a;用图表整理…

X射线底片焊缝缺陷检测

实现四种焊缝缺陷的检测和分割处理。

Python:谈谈常规滤波器(带通、低通、高通、带阻)的用法

一、滤波器的作用 滤波器在信号处理中用于移除或减少信号中的噪声&#xff0c;同时保持信号的某些特性。滤波器通常用于音频、视频和图像处理等领域。滤波器根据其 designed for different purposes and can be divided into several types, such as lowpass filters, highpass…

Pikachu 不安全的文件下载(Unsafe file download)概述 附漏洞利用案例

目录 获取下载链接 修改链接 重新构造链接 拓展 不安全的文件下载概述 文件下载功能在很多web系统上都会出现&#xff0c;一般我们当点击下载链接&#xff0c;便会向后台发送一个下载请求&#xff0c;一般这个请求会包含一个需要下载的文件名称&#xff0c;后台在收到请求…