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

Spring 事务详解:从基础到传播机制的实践指南

文章目录

    • 📕1. 事务回顾
    • 📕2. Spring 中事务的实现​
        • ✏️2.1 Spring 编程式事务(了解)​
        • ✏️2.2 Spring声明式事务
    • 📕3. @Transactional 详解​
        • ✏️3.1 rollbackFor​
        • ✏️3.2 事务隔离级别​
            • 🔖3.2.1 MySQL 事务隔离级别(回顾)​
            • 🔖3.2.2 Spring 事务隔离级别​
    • 📕4. Spring 事务传播机制​
        • ✏️4.1 什么是事务传播机制
        • ✏️4.2 事务的传播机制有哪些

特此注明 :
Designed By :长安城没有风
Version:1.0
Time:2025.08.09
Location:辽宁 · 阜新

📕1. 事务回顾

什么是事务?

事务是一组操作的集合,是一个不可分割的操作。事务会把所有的操作作为一个整体,一起向数据库提交或者是撤销操作请求,所以这组操作要么同时成功,要么同时失败。

为什么需要事务?

⽐如转账操作 :

第⼀步:A账⼾-100元
第⼆步:B账⼾+100元
如果没有事务,第⼀步执⾏成功了,第⼆步执⾏失败了,那么A账⼾的100元就平⽩⽆故消失了,如果使⽤事务就可以解决这个问题,让这⼀组操作要么⼀起成功,要么⼀起失败。

比如秒杀系统:

第⼀步:下单成功
第⼆步:扣减库存
下单成功后,库存也需要同步减少,如果下单成功,库存扣减失败,那么就会造成下单超出的情况。所以就需要把这两步操作放在同⼀个事务中,要么⼀起成功,要么⼀起失败。(理解事务概念为主,实际企业开发中,并不是简单的通过事务来处理。​)

事务的操作

事务的操作主要有三步:

  1. 开启事务:starttransaction/begin(⼀组操作前开启事务)
  2. 提交事务:commit(这组操作全部成功,提交事务)
  3. 回滚事务:rollback(这组操作中间任何⼀个操作出现异常,回滚事务)

📕2. Spring 中事务的实现​

✏️2.1 Spring 编程式事务(了解)​

Spring 编程式事务依赖以下核心接口:

  1. DataSourceTransactionManager : 事务管理器,专门用于处理基于 DataSource 的事务(如 JDBC、MyBatis 等持久层框架)。它实现了 PlatformTransactionManager 接口,核心功能是获取事务(开启事务)、提交事务、回滚事务,是编程式事务和声明式事务的底层核心组件。
  2. TransactionDefinition:定义事务属性(隔离级别、传播行为、超时时间、是否只读等)。
  3. TransactionStatus:表示当前事务的状态(是否活跃、是否已完成、是否需要回滚等)。
package xulong.com.captcha.controller;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import xulong.com.captcha.mapper.UserMybatisPlusMapper;
import xulong.com.captcha.model.UserInfo;@RestController
@RequestMapping("/user")
public class UserController {//事务管理器@Autowiredprivate DataSourceTransactionManager dataSourceTransactionManager;//定义事务属性@Autowiredprivate TransactionDefinition transactionDefinition;@Autowiredprivate UserMybatisPlusMapper userMybatisPlusMapper;@PostMapping("/register")public UserInfo register(@RequestBody UserInfo userInfo) {//开启事务TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);try {//执行数据库操作userMybatisPlusMapper.insert(userInfo);//提交事务dataSourceTransactionManager.commit(transactionStatus);} catch (Exception e) {//回滚事务dataSourceTransactionManager.rollback(transactionStatus);}return userInfo;}
}

以上代码虽然可以实现事务,但是操作十分繁琐,各位了解即可,下面我们介绍声明式事务。

✏️2.2 Spring声明式事务

在需要事务的方法上添加 @Transactional 注解就可以实现了,无需手动开启事务和提交事务,进入方法时自动开启事务,方法执行完会自动提交事务,如果中途发生了没有处理的异常会自动回滚事务。如果异常被程序捕获,⽅法就被认为是成功执⾏,依然会提交事务。

package xulong.com.captcha.controller;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import xulong.com.captcha.mapper.UserMybatisPlusMapper;
import xulong.com.captcha.model.UserInfo;@RestController
@RequestMapping("/user")
public class UserController {@Autowiredprivate UserMybatisPlusMapper userMybatisPlusMapper;@PostMapping("/register")@Transactionalpublic UserInfo register(@RequestBody UserInfo userInfo) {userMybatisPlusMapper.insert(userInfo);return userInfo;}
}

@Transactional 可以用来修饰方法或类 : ​

  1. 修饰⽅法时:只有修饰public⽅法时才⽣效(修饰其他⽅法时不会报错,也不⽣效)。
  2. 修饰类时:对 @Transactional 修饰的类中所有的public⽅法都⽣效。

运⾏程序,发现虽然程序出错了,但是由于异常被捕获了,所以事务依然得到了提交。如果需要事务进⾏回滚,有以下两种⽅式 :

重新抛出异常

package xulong.com.captcha.controller;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import xulong.com.captcha.mapper.UserMybatisPlusMapper;
import xulong.com.captcha.model.UserInfo;@RestController
@RequestMapping("/user")
public class UserController {@Autowiredprivate UserMybatisPlusMapper userMybatisPlusMapper;@PostMapping("/register")@Transactionalpublic UserInfo register(@RequestBody UserInfo userInfo) {userMybatisPlusMapper.insert(userInfo);try{int i = 1/0;}catch (Exception e){throw new RuntimeException("注册失败");}return userInfo;}
}

手动回滚事务

使用 TransactionAspectSupport.currentTransactionStatus() 得到当前的事务,并设置setRollbackOnly。

package xulong.com.captcha.controller;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import xulong.com.captcha.mapper.UserMybatisPlusMapper;
import xulong.com.captcha.model.UserInfo;@RestController
@RequestMapping("/user")
public class UserController {@Autowiredprivate UserMybatisPlusMapper userMybatisPlusMapper;@PostMapping("/register")@Transactionalpublic UserInfo register(@RequestBody UserInfo userInfo) {userMybatisPlusMapper.insert(userInfo);try{int i = 1/0;}catch (Exception e){TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();}return userInfo;}
}

📕3. @Transactional 详解​

✏️3.1 rollbackFor​

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

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

package xulong.com.captcha.controller;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import xulong.com.captcha.mapper.UserMybatisPlusMapper;
import xulong.com.captcha.model.UserInfo;@RestController
@RequestMapping("/user")
public class UserController {@Autowiredprivate UserMybatisPlusMapper userMybatisPlusMapper;@PostMapping("/register")@Transactional(rollbackFor = Exception.class)public UserInfo register(@RequestBody UserInfo userInfo) {userMybatisPlusMapper.insert(userInfo);try{int i = 1/0;}catch (Exception e){TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();}return userInfo;}
}

结论:

  1. 在Spring的事务管理中,默认只在遇到运行时异常RuntimeException和Error时才会回滚。
  2. 如果需要回滚指定类型的异常,可以通过rollbackFor属性来指定。
✏️3.2 事务隔离级别​
🔖3.2.1 MySQL 事务隔离级别(回顾)​

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

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

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

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

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

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

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

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

在这里插入图片描述

🔖3.2.2 Spring 事务隔离级别​

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

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

@RestController
@RequestMapping("/user")
public class UserController {@Autowiredprivate UserMybatisPlusMapper userMybatisPlusMapper;@PostMapping("/register")@Transactional(rollbackFor = Exception.class,isolation = Isolation.REPEATABLE_READ)public UserInfo register(@RequestBody UserInfo userInfo) {return userInfo;}
}

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

在这里插入图片描述

📕4. Spring 事务传播机制​

✏️4.1 什么是事务传播机制

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

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

⽐如公司流程管理,执⾏任务之前,需要先写执⾏⽂档,任务执⾏结束,再写总结汇报。
在这里插入图片描述
此时A部⻔有⼀项⼯作,需要B部⻔的⽀援,此时B部⻔是直接使⽤A部⻔的⽂档,还是新建⼀个⽂档呢?

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

在这里插入图片描述

✏️4.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。

比如一对新人要结婚了, 关于是否需要房子​

  1. Propagation.REQUIRED :需要有房⼦。如果你有房,我们就⼀起住,如果你没房,我们就⼀起买房。(如果当前存在事务,则加⼊该事务。如果当前没有事务,则创建⼀个新的事务。)
  2. Propagation.SUPPORTS :可以有房⼦。如果你有房,那就⼀起住。如果没房,那就租房。(如果当前存在事务,则加⼊该事务。如果当前没有事务,则以⾮事务的⽅式继续运⾏。)
  3. Propagation.MANDATORY :必须有房⼦。要求必须有房,如果没房就不结婚。(如果当前存在事务,则加⼊该事务。如果当前没有事务,则抛出异常。)
  4. Propagation.REQUIRES_NEW :必须买新房,不管你有没有房,必须要两个⼈⼀起买房。即使有房也不住。(创建⼀个新的事务。如果当前存在事务,则把当前事务挂起。)
  5. Propagation.NOT_SUPPORTED :不需要房。不管你有没有房,我都不住,必须租房。(以⾮事务⽅式运⾏,如果当前存在事务,则把当前事务挂起。)
  6. Propagation.NEVER :不能有房⼦。(以⾮事务⽅式运⾏,如果当前存在事务,则抛出异常。)
  7. Propagation.NESTED :如果你没房,就⼀起买房。如果你有房,我们就以房⼦为根据地,做点⽣意。(如果如果当前存在事务,则创建⼀个事务作为当前事务的嵌套事务来运⾏。如果当前没有事务,则该取值等价于 PROPAGATION_REQUIRED )
http://www.lryc.cn/news/620532.html

相关文章:

  • 【机器人-开发工具】ROS 2 (4)Jetson Nano 系统Ubuntu22.04安装ROS 2 Humble版本
  • Claude Code 国内直接使用,原生支持 Windows 免WSL安装教程
  • CVPR 2025 | 即插即用,动态场景深度感知新SOTA!单目视频精准SLAM+深度估计
  • Linux系统Namespace隔离实战:dd/mkfs/mount/unshare命令组合应用
  • 【iOS】KVC原理及自定义
  • 【KALI】第一篇 安装Kali Linux虚拟机之详细操作步骤讲解
  • Redis 从入门到生产:数据结构、持久化、集群、工程实践与避坑(含 Node.js/Python 示例)
  • Windows 安装 Claude Code 并将 Claude Code 的大模型替换为 Kimi 的完整步骤
  • 适用工业分选和工业应用的高光谱相机有哪些?什么品牌比较好?
  • 如何写出更清晰易读的布尔逻辑判断?
  • 【奔跑吧!Linux 内核(第二版)】第7章:系统调用的概念
  • 基于Java飞算AI的Spring Boot聊天室系统全流程实战
  • 在FP32输入上计算前向传播需要多长时间?FP16模型的实例与之前的模型相比,它快了多少?
  • 解刨HashMap的put流程 <二> JDK 1.8
  • 【自动驾驶】自动驾驶概述 ① ( 自动驾驶 与 无人驾驶 | 自动驾驶 相关岗位 及 技能需求 )
  • Day58--图论--117. 软件构建(卡码网),47. 参加科学大会(卡码网)
  • 从零开始的云计算生活——激流勇进,kubernetes模块之Pod资源对象
  • 解决EKS中KEDA访问AWS SQS权限问题:完整的IRSA配置指南
  • 【web站点安全开发】任务4:JavaScript与HTML/CSS的完美协作指南
  • 【论文阅读】基于卷积神经网络和预提取特征的肌电信号分类
  • 随身 Linux 开发环境:使用 cpolar 内网穿透服务实现 VSCode 远程访问
  • docker使用指定的MAC地址启动podman使用指定的MAC地址启动
  • vllmsglang 单端口多模型部署方案
  • 用飞算JavaAI一键生成电商平台项目:从需求到落地的高效实践
  • Java中加载语义模型
  • 【无标题】卷轴屏手机前瞻:三星/京东方柔性屏耐久性测试进展
  • 2025年世界职业院校技能大赛:项目简介模板
  • 工业一体机5G通讯IC/ID刷卡让MES系统管理更智能
  • SpringBoot 实现在线查看内存对象拓扑图 —— 给 JVM 装上“透视眼”
  • PostgreSQL + TimescaleDB 数据库语法配置