一、核心前置说明
- 传播机制:多个 @Transactional 方法内部调用,事务如何传递、创建、隔离
- 隔离级别:并发事务之间读写可见性,解决脏读 / 不可重复读 / 幻读
- 环境:
SpringBoot 2.5 + MySQL InnoDB - 关键前提:必须通过 Spring 代理调用(外部 Service 调用、不要本类 this 调用),事务才会生效
二、事务隔离级别(Isolation)+ 代码示例
1. 五种隔离级别枚举
public enum Isolation {
// 使用数据库默认隔离级别
DEFAULT,
// 读未提交:脏读+不可重复读+幻读都存在
READ_UNCOMMITTED,
// 读已提交:解决脏读,存在不可重复读、幻读
READ_COMMITTED,
// 可重复读:MySQL默认,解决脏读、不可重复读,存在幻读(间隙锁优化)
REPEATABLE_READ,
// 串行化:最高隔离,完全锁表,性能差
SERIALIZABLE
}
2. 业务代码配置隔离级别
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
@Service
public class OrderService {
/**
* 默认隔离级别:跟随MySQL → REPEATABLE_READ
*/
@Transactional
public void createOrderDefault() {
// 新增订单、扣库存
}
/**
* 读已提交:避免脏读,适合大多数金融、订单业务
*/
@Transactional(isolation = Isolation.READ_COMMITTED)
public void createOrderReadCommitted() {
// 业务操作
}
/**
* 可重复读:同事务多次查询结果一致
*/
@Transactional(isolation = Isolation.REPEATABLE_READ)
public void queryOrderRepeatableRead() {
// 多次查询订单数据
}
}
3. 并发问题对照(面试背诵)
- 脏读:读取到其他事务未提交数据
- 不可重复读:同一事务内,多次查询被其他事务
update修改 - 幻读:同一事务内,多次查询被其他事务
insert/delete改变行数
三、7 种事务传播机制(Propagation)+ 完整场景代码
1. 传播机制枚举含义
public enum Propagation {
REQUIRED, // 默认:有事务加入,无则新建
SUPPORTS, // 有事务加入,无则非事务运行
MANDATORY, // 强制必须在事务内,无事务直接报错
REQUIRES_NEW, // 新建独立事务,外部事务挂起,互不影响回滚
NOT_SUPPORTED, // 强制非事务运行,挂起外部事务
NEVER, // 强制非事务,有事务直接报错
NESTED // 嵌套事务:依赖外部事务,通过savepoint保存点
}
2. 分层业务 Service(模拟方法内部调用)
外层主业务
@Service
public class BusinessService {
private final LogService logService;
// 构造器注入
public BusinessService(LogService logService) {
this.logService = logService;
}
/**
* 外层主方法:默认 REQUIRED
*/
@Transactional(rollbackFor = Exception.class)
public void mainBusiness() throws Exception {
// 1. 主业务入库
System.out.println("外层主业务执行");
// 2. 调用内部日志方法
logService.saveOperateLog();
// 模拟业务异常
int a = 1 / 0;
}
}
内层不同传播机制实现类
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@Service
public class LogService {
// 1. REQUIRED 默认:共用外层事务,外层异常整体回滚
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void saveOperateLog() {
System.out.println("REQUIRED:保存操作日志");
}
// 2. REQUIRES_NEW:独立新事务,外层回滚、日志依然保留
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
public void saveLogIndependent() {
System.out.println("REQUIRES_NEW:独立日志事务");
}
// 3. NESTED 嵌套事务:依赖外层事务,内部可局部回滚
@Transactional(propagation = Propagation.NESTED, rollbackFor = Exception.class)
public void nestedLog() {
System.out.println("NESTED:嵌套保存点事务");
}
// 4. NOT_SUPPORTED:强制非事务执行
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void noTxOperate() {
System.out.println("NOT_SUPPORTED:无事务执行");
}
// 5. SUPPORTS:有事务共用,无事务裸跑
@Transactional(propagation = Propagation.SUPPORTS)
public void supportQuery() {
System.out.println("SUPPORTS:查询业务");
}
}
四、高频场景关键差异(代码对应落地)
场景 1:REQUIRED(默认)
- 外层有事务 → 合并同一个事务
- 外层抛异常 → 内层、外层全部回滚
- 适用:90% 增删改业务
场景 2:REQUIRES_NEW【高频面试 + 实战】
修改外层调用代码:
@Transactional(rollbackFor = Exception.class)
public void mainBusiness() {
System.out.println("外层主业务");
// 独立事务,不受外层异常影响
logService.saveLogIndependent();
// 主业务异常
int i = 1 / 0;
}
- 结果:主业务回滚,日志数据正常提交
- 适用:操作日志、监控记录、消息投递,不能因为主业务失败丢失日志
场景 3:NESTED 嵌套事务
@Transactional(rollbackFor = Exception.class)
public void nestedDemo() {
// 主业务
// 嵌套子事务
try {
logService.nestedLog();
int a = 1 / 0;
} catch (Exception e) {
// 子事务局部回滚,主事务可正常提交
e.printStackTrace();
}
}
- 依靠
savepoint保存点 - 子异常可单独回滚,外层正常提交;外层回滚,子事务强制回滚
场景 4:NOT_SUPPORTED 非事务
@Transactional
public void testNoSupport() {
// 此段无事务,即时写入数据库
logService.noTxOperate();
// 后续异常,上面数据不会回滚
int a = 1 / 0;
}
五、事务失效经典坑(代码避坑)
-
本类 this 调用 事务方法,不走代理,事务失效
// 错误写法 public void outer() { this.innerTx(); } @Transactional public void innerTx(){} -
异常不是
RuntimeException / Error,未配置rollbackFor// 正确写法 @Transactional(rollbackFor = Exception.class) -
方法权限为
private,无法被 Spring 代理
六、面试极简总结(代码版)
- 隔离级别:控制并发事务 数据可见性,MySQL 默认
REPEATABLE_READ - 传播机制:控制方法嵌套调用事务归属
- 日常开发:
- 增删改:
@Transactional(rollbackFor = Exception.class) - 日志独立:
Propagation.REQUIRES_NEW - 查询接口:
Propagation.SUPPORTS
- 增删改:
- 核心区别:
REQUIRES_NEW:完全独立事务NESTED:依赖外层、保存点局部回滚