Spring事务(声明式事务)(Spring的事务,Spring隔离级别,事务传播机制)
目录
一、什么是事务,为什么要用事务
二、Spring声明式事务
🍅 1、@Transactional的使用
🎈 事务回滚
🎈注意:异常被捕获,不会发生事务回滚
🍅 2、@Transactional 作⽤范围
🍅 3、@Transactional 参数说明
🍅 4、@Transactional的工作原理
三、事务的特性以及隔离级别(重要)
🍅 事务的特性(ACID)
🍅 Spring隔离级别
四、Spring事务的传播机制
🍅为什么需要事务传播机制
🍅 事务传播机制的种类
🎈Propagation.REQUIRED:
🎈 Propagation.SUPPORTS
🎈 Propagation.NEVER
🎈Propagation.NESTED
一、什么是事务,为什么要用事务
定义:事务就是将一组操作封装成一个执行单元(封装到一起),要么全部成功,要么全部失败
为什么要用事务?举例如下:
转账分为两步:
第一步:A账户:-100¥
第二部:B账户:+100¥
如果没有事务,那么,第一步执行成功,第二部失败,就会导致100¥不见了,不知道转哪里去了。但是当有了事务,着两步操作要么一起成功要么一起失败,这样就可以解决这个问题
分类:Spring中的事务操作分为两类:
- 编程式事务(手动写代码操作事务,不常用,复杂)
- 声明式事务(利用注解自动开启和提交事务,常用,简便)
下面主要介绍声明式事务
二、Spring声明式事务
🍅 1、@Transactional的使用
🎈 事务回滚
声明式事务的使用很简单,只需要在方法上添加@Transactional注解就可以实现了
不需要手动开启事务和提交事务,进入方法时自动开启事务,方法执行完就会自动提交事务,如果中途发生了没有处理的异常就会自动回滚事务。
代码示例:
@Data
public class UserInfo {private int id;private String username;private String password;private String photo;private LocalDateTime createtime;private LocalDateTime updatetime;private int state;
}
@Mapper
public interface UserMapper {@Insert("insert into userinfo(username,password) values (#{username},#{password})")int add(UserInfo userInfo);
}
@Service
public class UserService {@Autowiredprivate UserMapper userMapper;public int add(UserInfo userInfo){return userMapper.add(userInfo);}
}
@RequestMapping("/user")
@RestController
public class UserController {@Autowiredprivate UserService userService;@RequestMapping("/add")@Transactionalpublic int add(){
// 1.非空判断UserInfo userInfo = new UserInfo();userInfo.setUsername("huhu");userInfo.setPassword("123");
// 2.调用service执行添加int result = userService.add(userInfo);System.out.println("result:" + result);int num = 10/0;
// 3.将结果给前端return result;}
}
执行以上代码:结果如下,发生算数异常

但是sql执行没有异常:

这时候我们查看以下数据库有没有该数据,有没有进行数据回滚?
图中并没有我添加的数据,由此可见,@Transactional注解进行了数据回滚。
🎈注意:异常被捕获,不会发生事务回滚
问题:当自己把程序捕获以后,代表着事务不会发现程序发生了异常,在这种情况下,事务不会发生回滚。
@RequestMapping("/add")@Transactionalpublic int add(){
// 1.非空判断UserInfo userInfo = new UserInfo();userInfo.setUsername("huhu");userInfo.setPassword("123");
// 2.调用service执行添加int result = userService.add(userInfo);System.out.println("result:" + result);try{int num = 10/0;}catch (Exception e){}
// 3.将结果给前端return result;}

解决方案1: 将异常继续抛出去(代理对象就能感知到异常,也能够回滚到事务)
@RequestMapping("/add")@Transactionalpublic int add(){ // 1.非空判断UserInfo userInfo = new UserInfo();userInfo.setUsername("万叶");userInfo.setPassword("123"); // 2.调用service执行添加int result = userService.add(userInfo);System.out.println("result:" + result);try{int num = 10/0;}catch (Exception e){throw e;} // 3.将结果给前端return result;}新增的信息不在里面:万叶的信息
解决方案2:使用代码手动回滚事务
@RequestMapping("/add")@Transactionalpublic int add(){ // 1.非空判断UserInfo userInfo = new UserInfo();userInfo.setUsername("万叶");userInfo.setPassword("123"); // 2.调用service执行添加int result = userService.add(userInfo);System.out.println("result:" + result);try{int num = 10/0;}catch (Exception e){TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();} // 3.将结果给前端return result;}
🍅 2、@Transactional 作⽤范围
@Transactional 可以用来修饰类或者方法(public)
- 修饰方法时,只能应用到public方法上面,否则不生效
- 修饰类时,表明该注解对该类的所有public方法都生效
🍅 3、@Transactional 参数说明
一般情况下,以下参数都是默认的

🍅 4、@Transactional的工作原理
@Transactional是基于AOP实现的,AOP又是基于动态代理实现的。
如果目标对象实现了接口,默认情况下就会采用JDK的动态代理,如果目标对象没有实现接口,会使用CGLIB的动态代理。
@Transactional在开始执行业务之前,通过代理先开始事务,在执行成功之后再提交事务。如果中途遇见异常,则回滚事务。
@Transactional实现思路预览:

@Transactional具体执行细节如下图所示:

三、事务的特性以及隔离级别(重要)
🎄 脏读:一个事务读取到了另一个事务修改的数据以后,后一个事务又进行了回滚操作,从而导致第一个事务读取到的数据是错误的。
🎄 不可重复读:一个事务两次查询得到的结果不同,因为在两次查询中间,有另一个事务把数据修改了
🎄 幻读:一个事务两次查询中得到的结果集不同,因为在两次查询中另一个事务又新增了一部分数据。
🍅 事务的特性(ACID)
🎄 原子性(不可分割性)(Atomicity):一个事务中的所有操作,要么全部完成,要么全部不完成,不会结束中间某个环节。事务在执行过程中发生错误,就会回滚到事务开始前的状态,相当这个事务从来没有执行过
🎄 一致性(Consistency):在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写入的资料必须完全符合所有的预设规则,包含资料的精确度、串联性以及后续数据库可以自发性的完成预定的工作。
🎄 持久性(Isolation):事务处理结束以后,对数据的修改就是永久的,即便系统故障也不会丢失
🎄 隔离性(Durability):数据库允许多个并发事务同时对齐数据进行读写和修改的能力,隔离性可以放置多个事务并发执行时由于交叉执行导致数据的不一致。事务的隔离分为不同级别,包括读未提交,读提交,可重复读,串行化。
🍅 Spring隔离级别
Spring事务隔离级别有五种:
🎄 Isolation.DEFAULT:以连接的数据库的事务隔离级别为主
🎄 Isolation.READ_UNCOMMITTED:读未提交,可以读取到未提交的事务,存在脏读
🎄 Isolation.READ_COMMITTED:读已提交,只能读取到已经提交的事务,解决了脏读,存在不可重复度。
🎄 Isolation.REPEATABLE_READ:可重复读,解决了不可重复度,但是存在幻读。
🎄 Isolation.SERIALIZABLE:串行化,可以解决所有并发问题,但是性能太低。
默认情况下,是以SQL的事务隔离级别为主的(Isolation.DEFAULT)。
但是当Sping设置了事务隔离级别以后,就会以Spring的事务隔离级别为主。以下就是以SERIALIZABLE为主的。
@RequestMapping("/add")@Transactional(isolation = Isolation.SERIALIZABLE)public int add(){
}
事务的隔离级别保证了多个并发事务执行的可控性。

四、Spring事务的传播机制
定义:事务的传播机制定义了多个包含事务的方法,相互调用时,事务是如何在这些方法间进行传递的。(规定多个事务在相互调用时,事务的执行行为)
🍅为什么需要事务传播机制
事务传播机制是保证一个事务在多个调用方法间的可控性的(稳定性)
事务的传播机制解决的是一个事务在多个节点(方法)中传递的问题:
🍅 事务传播机制的种类
🎄 Propagation.REQUIRED:默认的事务传播级别,表示如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务
🎄 Propagation.SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行
🎄 Propagation.MANDATORY:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常
🎄 Propagation.REQUIRES_NEW:表示创建一个新的事务,如果当前存在事务,则把当前事务挂起。也就是说不管外部方法是否开启事务,Propagation.REQUIRES_NEW修饰的内部方法会新开启自己的事务,且开启的事务相互独立互不干扰。
🎄 Propagation.NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起
🎄 Propagation.NEVER:以非事务方式运行,如果当前存在事务,则抛出异常
🎄 Propagation.NESTED:如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于Propagation.REQUIRED
🎈Propagation.REQUIRED:
默认的事务传播级别,表示如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务
示例:
UserService:
@Transactionalpublic int add(UserInfo userInfo){int result = userMapper.add(userInfo);System.out.println("add result -> "+result);insert(userInfo);return result;}@Transactionalpublic int insert(UserInfo userInfo){int result = userMapper.add(userInfo);System.out.println("insert resullt -> "+ result);int num = 10 / 0;return result;}
Controller:
@RequestMapping("/add")@Transactional(propagation = Propagation.REQUIRED)public int add(){
// 1.非空判断UserInfo userInfo = new UserInfo();userInfo.setUsername("万叶");userInfo.setPassword("123");
// 2.调用service执行添加int result = userService.add(userInfo);return result;}
此时结果是,报错是算数异常,进行了事务回滚,数据库中没有添加任何数据

🎈 Propagation.SUPPORTS
如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行
示例:
UserService
@Transactional(propagation = Propagation.SUPPORTS)public int add(UserInfo userInfo){int result = userMapper.add(userInfo);System.out.println("add result -> "+result);insert(userInfo);return result;}@Transactional(propagation = Propagation.SUPPORTS)public int insert(UserInfo userInfo){int result = userMapper.add(userInfo);System.out.println("insert resullt -> "+ result);int num = 10 / 0;return result;}
UserController:
@Transactional(propagation = Propagation.SUPPORTS)public int add(){
// 1.非空判断UserInfo userInfo = new UserInfo();userInfo.setUsername("万叶");userInfo.setPassword("123");
// 2.调用service执行添加int result = userService.add(userInfo);return result;}
当前调用链不存在事务,结果是数据库中添加了两条数据,并且报错是算数异常,但是并没有进行数据回滚

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

UserController:
@RequestMapping("/add")
//调用链存在事务@Transactional(propagation = Propagation.REQUIRED)public int add(){
// 1.非空判断UserInfo userInfo = new UserInfo();userInfo.setUsername("万叶");userInfo.setPassword("123");
// 2.调用service执行添加int result = userService.add(userInfo);return result;}
USerService:
@Transactional(propagation = Propagation.NEVER)public int add(UserInfo userInfo){int result = userMapper.add(userInfo);System.out.println("add result -> "+result);insert(userInfo);return result;}@Transactional(propagation = Propagation.NEVER)public int insert(UserInfo userInfo){int result = userMapper.add(userInfo);System.out.println("insert resullt -> "+ result);int num = 10 / 0;return result;}

说明程序在发现有事务以后,就没有运行了,不存在事务回滚,而是发现事务以后,程序就没有运行,而是直接抛出异常。
🎈Propagation.NESTED
如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于Propagation.REQUIRED

示例:
UserService:
注意:为什么要try-catch
如果不try-catch就会报错,报错就会使得整个程序进行回滚,这里为了使得满足NESTED的情况,就进行了try-catch,就能使得回滚到事务保存点。
@Transactional(propagation = Propagation.NESTED)public int add(UserInfo userInfo){int result = userMapper.add(userInfo);System.out.println("add result -> "+result);return result;}@Transactional(propagation = Propagation.NESTED)public int insert(UserInfo userInfo){int result = userMapper.add(userInfo);System.out.println("insert resullt -> "+ result);try{int num = 10 / 0;}catch (Exception e){TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();}return result;}
UserController:
@RequestMapping("/add")@Transactional(propagation = Propagation.REQUIRED)public int add(){
// 1.非空判断UserInfo userInfo = new UserInfo();userInfo.setUsername("影");userInfo.setPassword("123");
// 2.调用service执行添加int result = userService.add(userInfo);userService.insert(userInfo);return result;}
结果如下:

相关文章:
Spring事务(声明式事务)(Spring的事务,Spring隔离级别,事务传播机制)
目录 一、什么是事务,为什么要用事务 二、Spring声明式事务 🍅 1、Transactional的使用 🎈 事务回滚 🎈注意:异常被捕获,不会发生事务回滚 🍅 2、Transactional 作⽤范围 🍅 …...
Linux运维面试题(四)之Linux服务管理
Linux运维面试题(四)之Linux服务管理 4.1 SSHSSH的登录验证方式SSH的登陆端口(默认22)和监听设置(/etc/ssh/sshd_config)SSH的登录用户限制(/etc/ssh/sshd_config PermitRootLogin)SSH的登录超时设置(/etc/…...
ChatGPT能否撰写科研论文?
ChatGPT,这款被许多人誉为语言处理领域的“黑马”,究竟能否应用于撰写科研论文?近期,以色列理工学院生物学家兼数据科学家Roy Kishony带领的团队,针对这一问题进行了系列研究,其结果已在《Nature》杂志上发…...
2023 电赛 E 题 K210方案
第一章:K210 介绍 K210芯片是一款基于RISC-V架构的嵌入式人工智能芯片,具备低功耗、高性能的特点。它拥有强大的图像处理和机器学习能力,适用于边缘计算设备和物联网应用。为了方便开发者,K210芯片提供了丰富的外设接口ÿ…...
网络知识介绍
一、TCP 传输控制协议,Transmission Control Protocol。 面向广域网的通信协议,跨域多个网络通信时,为两个通信端点之间提供一条具有如下特点的通信方式: 基于流、面向连接、可靠通信方式、网络状况不佳时尽量降低系统由于重传带…...
MapStruct设置全局的ComponentModel
在mapStruct上边,如果我们要切换成非默认的组件模式,常常要在Mapper注释中添加componentModel "spring",如果类太多的了的话,非常麻烦,有没有更好的方式呢,有的,可以在pom中添加一个…...
LinearAlgebraMIT_6_ColumnSpaceAndNullSpace
这节课的两个重点是column space列空间和null space零空间。 x.1 pre-multiply/left multiply and post-multiply/right multiply 对于pre-multiply/left multiply左乘和post-multiply/right multiply右乘,如果用英文的pre-和post-是比较容易理解的, A…...
出版物经营许可办理 出版物许可地址变更 出版物零售延期
一、出版物零售单位设立所需材料 1、申请书 2、营业执照 3、租赁合同 4、主要负责人身 份证 5、出版物经营许可申请表 二、办理出版物经营许可证所要符合的条件 1、有确定的企业名称和经营范围; 2、有出版物业务的经营场地; 3、有出版物业务的组织机构和发行人员。 三、…...
【LeetCode每日一题】——807.保持城市天际线
文章目录 一【题目类别】二【题目难度】三【题目编号】四【题目描述】五【题目示例】六【题目提示】七【解题思路】八【时间频度】九【代码实现】十【提交结果】 一【题目类别】 矩阵 二【题目难度】 中等 三【题目编号】 1572.矩阵对角线元素的和 四【题目描述】 给你一…...
JavaScript--Date(日期)对象
介绍和说明 创建一个Date对象并获取当前日期和时间: 使用new Date()语句可以创建一个表示当前日期和时间的Date对象。它将使用客户端设备上的当前日期和时间。例如:const currentDate new Date(); 获取特定日期的年、月、日、小时、分钟、秒࿱…...
一文讲清多线程与多线程同步
1 多线程 1.1 线程的概念 十多年前,主流观点主张在可能的情况下优先选择多进程而非多线程,如今,多线程编程已经成为编程领域的事实标准。多线程技术在很大程度上改善了程序的性能和响应能力,使其能够更加高效地利用系统资源&…...
《Java-SE-第二十六章》之线程池
前言 在你立足处深挖下去,就会有泉水涌出!别管蒙昧者们叫嚷:“下边永远是地狱!” 博客主页:KC老衲爱尼姑的博客主页 博主的github,平常所写代码皆在于此 共勉:talk is cheap, show me the code 作者是爪哇岛的新手,水平很有限&…...
【数据库】将excel数据导入mysql数据库
环境:Windows10 mysql8以上 将你要导入的excel表另存为txt格式 打开txt格式文件,删除表头行并另存为并更改编码方式(由于与数据库的编码不同,会导致导入报错) 通过命令行登录数据库 winr cmd进入 进入装mysql的目录位…...
无涯教程-Lua - repeat...until 语句函数
与 for 和 while 循环(它们在循环顶部测试循环条件)不同,Lua编程中的 repeat ... until 循环语言在循环的底部检查其条件。 repeat ... until 循环与while循环相似,不同之处在于,保证do ... while循环至少执行一次。 repeat...until loop - …...
环形链表 LeetCode热题100
题目 给你一个链表的头节点 head ,判断链表中是否有环。 思路 快慢指针。开始快指针在慢指针前面,当快指针等于慢指针时说明有环,如果快指针指向null时说明无环。 代码 /*** Definition for singly-linked list.* struct ListNode {* …...
使用python将每组两行数据合并一行
1、使用场景 将有规律的每组(一组2行)的单数行和双数行合并为一行,以空格分割。 比如使用pssh批量得出的结果,想让ip行和结果行合并为一行(前提如上所述) [rootk8s-master1 tmp]# pssh -h iplist -i hostname [1] 18:12:42 [SU…...
14-1_Qt 5.9 C++开发指南_网络编程及主机信息查询_HostInfo
Qt 网络模块提供了用于编写 TCP/IP 客户端和服务器端程序的各种类,如用于 TCP 通信的QTcpSocket 和 QTcpServer,用于 UDP 通信的 QUdpSocket,还有用于实现 HTTP、FTP 等普通网络协议的高级类如 QNetworkRequest,QNetworkReply 和Q…...
【iOS】通知原理
我们可以通过看通知的实现机制来了解通知中心是怎么实现对观察者的引用的。由于苹果对Foundation源码是不开源的,我们具体就参考一下GNUStep的源码实现。GNUStep的源码地址为:GNUStep源码GitHub下载地址, 具体源码可以进行查看。 通知的主要流程 通知全…...
创建邮件服务器(小微企业)
这里写自定义目录标题 目的硬件选型:软件选型:coremail (商业版本)postfixumail免费开源版本新的改变功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适…...
android app控制ros机器人四(调整界面布局)
半吊子改安卓,记录页面布局调整: 在ros-mobile基础上顶端增加一行,用于显示app名称和logo图像;修改标签页。 添加文字简单,但是替换图标长知识了,开始只是简单的把mipmap各个文件夹下的图片进行替换&…...
2026年10款论文降AI率平台实测:从90%降至10%的硬核之选
现在学校对 AIGC 的检测越来越严格,降低 AI 率成了毕业生最头疼的问题。我当初写论文的时候,就因为 AI 率太高差点栽跟头,熬夜一遍遍手动修改,结果不仅 AI 率没降下来,查重率还越改越高,整个人都快崩溃了。…...
Tauri + GitHub Actions 自动化打包指南:如何为你的桌面应用配置跨平台自动更新
Tauri GitHub Actions 自动化打包与更新体系构建指南 当你的Tauri应用从开发阶段进入产品化阶段时,如何确保用户能够无缝获取最新功能和安全更新,成为影响产品体验的关键因素。本文将带你构建一个完整的自动化打包与更新体系,从签名机制到发…...
录音总结会议纪要推荐,零基础新手避坑可直接上手指南
这是专为零基础新手整理的2026年录音转会议纪要避坑指南,适配喜欢尝试效率工具、想借助AI节省整理时间的朋友,所有推荐均按实际场景适配度排序,内容简洁易懂,看完可直接上手,无需自行试错踩坑。很多新手接触录音转会议…...
免费本地语音识别的终极解决方案:3步实现完全离线实时语音转文字
免费本地语音识别的终极解决方案:3步实现完全离线实时语音转文字 【免费下载链接】TMSpeech 腾讯会议摸鱼工具 项目地址: https://gitcode.com/gh_mirrors/tm/TMSpeech 在数字化办公和在线学习日益普及的今天,你是否还在为云端语音识别服务的隐私…...
FPGA资源吃紧?看Artix7-35T如何“精打细算”实现MIPI视频解码与HDMI输出
Artix7-35T极限优化:在资源受限FPGA上实现MIPI-HDMI全流程处理 当医疗内窥镜或工业检测设备需要嵌入式图像处理时,工程师们常常面临一个残酷的现实:既要实现复杂的MIPI视频处理流水线,又不得不使用Artix7-35T这类入门级FPGA。这颗…...
LAV Filters终极指南:深度解析开源DirectShow解码器的架构原理与实战配置
LAV Filters终极指南:深度解析开源DirectShow解码器的架构原理与实战配置 【免费下载链接】LAVFilters LAV Filters - Open-Source DirectShow Media Splitter and Decoders 项目地址: https://gitcode.com/gh_mirrors/la/LAVFilters LAV Filters是一套基于F…...
从Wi-Fi 6到5G:深入浅出聊聊MIMO中的CSI反馈那些事儿(PMI/RI/CQI详解)
从Wi-Fi 6到5G:深入浅出聊聊MIMO中的CSI反馈那些事儿(PMI/RI/CQI详解) 现代无线通信系统正经历着从Wi-Fi 6到5G的跨越式发展,而多天线技术(MIMO)作为提升频谱效率的核心手段,其性能很大程度上依赖于准确的信道状态信息…...
【条件对抗生成网络】从理论到实践:CGAN如何实现可控图像生成
1. 条件对抗生成网络(CGAN)是什么? 想象一下,你正在教一个小朋友画画。普通GAN(生成对抗网络)就像让小朋友随意涂鸦,画出来的内容完全随机;而CGAN则像是你给小朋友一个明确的主题&am…...
从MATLAB函数到Python字典:一个脚本搞定MATPOWER数据格式转换与可视化
从MATLAB函数到Python字典:电力系统数据跨平台处理实战 电力系统分析领域长期依赖MATLAB生态,而MATPOWER作为经典工具包更是以.m函数文件作为标准数据载体。但当我们需要结合Python强大的数据处理和可视化能力时,这种数据格式就成为了技术栈融…...
如何在3分钟内搭建Excel MCP Server:无需安装Microsoft Excel的终极指南
如何在3分钟内搭建Excel MCP Server:无需安装Microsoft Excel的终极指南 【免费下载链接】excel-mcp-server A Model Context Protocol server for Excel file manipulation 项目地址: https://gitcode.com/gh_mirrors/ex/excel-mcp-server 还在为没有Micros…...




