Java复习事务相关 mysql事务隔离级别 spring事务的传播机制 2025年11月9日
目录
MySQL 事务隔离级别
事务隔离级别
可能会产生的问题
概述
图片详解
Spring 事务传播机制
概念
速记
图解
举例
例子1:REQUIRED 传播机制(默认机制)
例子2:REQUIRES_NEW 传播机制
思考与总结
MySQL 事务隔离级别
事务隔离级别
读未提交
允许读取尚未提交的数据变更,
可能会导致脏读,不可重复读和幻读
读已提交
允许读取并发事务已经提交的数据,
可以阻止脏读,可能会导致不可重复读和幻读
可重复读
对同一字段的多次读取结果是一样的
这是 inoodb 存储引擎默认的事务隔离级别
可串行化
所有事务依次逐个执行,
此级别可以防止脏读,不可重复读和幻读
可能会产生的问题
脏读
脏读又叫读未提交
脏读就是读取到了第一个事务未提交的数据
不可重复读
一个事务多次读取一条数据,但是这个期间,有另一个事务修改了数据并进行了提交
这时候这个事务在多次读取这一条数据的时候就会出现不同的结果
幻读
一个事务多次读取一个数据页的数据行数,但此时有另一个事务添加了一条数据
这时候这个事务在多次查询数据的总行数就会出现不同的结果
概述
脏读又叫读未提交
读已提交 的事务隔离级别解决了 脏读 的问题
可重复读 的事务隔离级别解决了 不可重复读 的问题
脏读读取的是另一个事务尚未提交的数据,而可重复读是读取到了另一个事务已经提交的数据
可重复读关注的是同一个事务在一个时间范围内读取的数据内容发生了变化,而幻读关注的是同一个事务在一个时间范围内读取到的数据行数发生了变化
图片详解

Spring 事务传播机制
概念
spring 的事务传播机制是指 多个事务的方法在互相调用的时候
事务是如何在这些方法之间传递的
通俗点讲就是 有两个事务 方法 A 开启了事务 而在调用过程中调用了开启事务的 B 方法
这时候 B 方法是应该加入到 A 的事务当中呢
还是两个事务互不干扰
还是 B 事务需要的是嵌套到 A 事务中执行呢
这就是事务的传播机制
速记
spring 的事务传播级别可以分为三个维度
- 支持当前事务
如果有事务 就加入当前事务 如果没有事务 就自己创建一个新事务 然后加入这个事务 (默认的事务隔离级别)
如果有事务 就加入当前事务 如果没有事务 就自己以非事务的方式执行
如果有事务 就加入当前事务 如果没有事务 就直接抛出异常
- 不支持当前事务
自己新建一个事务并以事务的方式执行 如果有事务 就把先前的事务挂起
自己以非事务的方式执行 如果有事务 就先把先前的事务挂起
以非事务的方式执行 如果当前存在事务 就抛出异常
- 嵌套事务
如果当前存在事务 就在这个事务的基础上嵌套事务执行 如果没有事务 就新建一个事务并且加入事务
每个子事务(嵌套部分)有自己的保存点(savepoint),子事务回滚时,只会回滚到该保存点,不影响外部事务的其他操作;而外部事务回滚时,会连带子事务一起回滚。
图解

生动形象

在Spring事务管理中,事务传播机制定义了多个带有事务的方法相互调用时,事务如何传递和生效。以下是两个通俗易懂的业务例子,分别对应两种常用的传播机制:
举例
默认的事务传播机制是为了保证强一致性的情况
比如说电商下单我们要保证创建订单和扣减库存这两个操作并为一个原子操作
例子1:REQUIRED 传播机制(默认机制)
业务场景:电商下单流程
- 有两个核心方法:
createOrder()(创建订单)和deductStock()(扣减库存),都需要事务保证数据一致性。 - 业务规则:下单和扣减库存必须在同一个事务中,要么都成功,要么都失败。
代码逻辑:
@Service
public class OrderService {@Autowiredprivate StockService stockService;// 传播机制默认是 REQUIRED:如果当前有事务,就加入;没有就新建@Transactional(propagation = Propagation.REQUIRED)public void createOrder() {// 1. 保存订单信息到数据库saveOrder(); // 2. 调用扣减库存的方法(带事务)stockService.deductStock(); }
}@Service
public class StockService {// 传播机制为 REQUIRED@Transactional(propagation = Propagation.REQUIRED)public void deductStock() {// 扣减商品库存updateStock(); }
}
效果:
- 当
createOrder()调用deductStock()时,由于createOrder()已经开启了事务,deductStock()会直接加入这个事务。 - 如果扣减库存失败(比如库存不足抛异常),整个事务会回滚:订单不会保存,库存也不会扣减,符合业务预期。
例子2:REQUIRES_NEW 传播机制
考虑一下业务场景 现在有用户注册和添加积分
用户注册是核心业务 送积分只是附加业务
我们必须要确保一种情况就是 即使送积分失败了 我们的用户也要注册成功 不会进行回滚数据库等一系列操作
业务场景:用户注册送积分
- 有两个方法:
registerUser()(注册用户)和addPoints()(添加积分),都需要事务。 - 业务规则:即使积分添加失败,用户注册也必须成功(注册是核心流程,积分是附加福利)。
代码逻辑:
@Service
public class UserService {@Autowiredprivate PointsService pointsService;// 传播机制为 REQUIRED:创建用户的事务@Transactional(propagation = Propagation.REQUIRED)public void registerUser() {// 1. 保存用户信息到数据库saveUser(); // 2. 调用添加积分的方法(独立事务)try {pointsService.addPoints(); } catch (Exception e) {// 积分添加失败不影响用户注册,记录日志即可log.error("积分添加失败", e);}}
}@Service
public class PointsService {// 传播机制为 REQUIRES_NEW:无论当前是否有事务,都新建一个独立事务@Transactional(propagation = Propagation.REQUIRES_NEW)public void addPoints() {// 给新用户添加积分savePoints(); // 假设这里抛异常(比如积分系统临时故障)throw new RuntimeException("积分添加失败");}
}
效果:
registerUser()开启了一个事务,调用addPoints()时,由于其传播机制是REQUIRES_NEW,会暂停当前事务,新建一个独立的积分事务。- 当
addPoints()抛异常时,只有积分事务回滚(积分不保存),而用户注册的事务不受影响,继续提交(用户成功注册),符合“注册优先”的业务规则。
思考与总结
我们该怎么确定我们该用哪个事务传播机制呢
假如我们在 spring 中有两个业务事务 A 和 B
我们首先最主要考虑的是事务 B 是不是要强制依赖于事务 A 是不是跟先前那个事务有关系
如果有是这样的代表我们的 事务 B 肯定会加入事务 A 成为同生共死的一个逻辑
还是说事务 B 其实在业务上没有必要依赖于事务 A 无论如何我都要新建一个新的事务 进行它自己的逻辑
还是我要进行嵌套处理 让他在嵌套事务内执行 在逻辑上形成一个上下级的关系 内部事务回滚会有自己单独的保存点 外部事务回滚会将内部所有事务的状态回到最初状态
