一、SpringBoot事务机制:用"公司报销流程"理解传播机制
1. 事务传播的本质场景
想象你在公司处理报销流程:
- 当你在处理部门报销时(主事务)
- 突然需要处理紧急的差旅报销(子事务)
- 两个报销如何处理关系?这就是事务传播要解决的问题
2. 七大传播级别大白话解析
① REQUIRED(默认值):接力盖章
@Transactional(propagation = Propagation.REQUIRED)
public void mainMethod() {
// 主流程开始盖章(开启事务)
subMethod(); // 子方法使用REQUIRED
}
@Transactional(propagation = Propagation.REQUIRED)
public void subMethod() {
// 发现已有盖章,直接使用同一个章
// 任何一个环节出错,整个报销单作废
}
比喻:部门报销进行到一半,新增差旅报销时直接在同一张报销单上继续填写。任一错误整单重填。
② REQUIRES_NEW:另起炉灶
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void subMethod() {
// 无论主流程是否盖章,都启用新报销单
// 新报销单的成败不影响主流程
}
比喻:主报销单处理时,突然需要处理紧急报销,立即启用新报销单单独处理,两个报销互不影响。
③ NESTED:子流程保险箱
@Transactional(propagation = Propagation.NESTED)
public void subMethod() {
// 在主报销单内创建存档点
// 子流程失败只回滚存档点之后的操作
}
比喻:主报销单处理时,将当前进度拍照存档。子流程失败时,只恢复到存档点,不影响存档前的操作。
④ MANDATORY:必须搭车
@Transactional(propagation = Propagation.MANDATORY)
public void subMethod() {
// 必须存在主报销单才能执行
// 单独调用会抛出异常
}
比喻:补充材料必须依附于主报销单存在,单独提交补充材料会被财务直接驳回。
⑤ SUPPORTS:随波逐流
@Transactional(propagation = Propagation.SUPPORTS)
public void subMethod() {
// 有主报销单就跟着用
// 没有就普通流程处理
}
比喻:普通文件审批,如果有报销流程就跟着走流程,没有就按常规处理。
⑥ NOT_SUPPORTED:拒绝参与
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void subMethod() {
// 暂停当前报销流程
// 以非事务方式执行
}
比喻:处理报销时需要查询外部系统,此时暂停报销流程,直接进行查询操作。
⑦ NEVER:严禁参与
@Transactional(propagation = Propagation.NEVER)
public void subMethod() {
// 严禁在事务中调用
// 否则直接报错
}
比喻:机密文件处理时,如果发现被包含在常规报销流程中,立即终止操作。
3. 事务失效的六大陷阱
① 自调用陷阱
public class OrderService {
public void createOrder() {
validateStock(); // 自调用失效!
}
@Transactional
private void validateStock() {
// 事务注解不生效
}
}
解决方法:注入自身Bean
@Autowired
private OrderService self;
public void createOrder() {
self.validateStock(); // 通过代理调用
}
② 异常类型不匹配
@Transactional
public void updateData() throws Exception {
try {
// 数据库操作
} catch (Exception e) {
throw new Exception("错误"); // 非RuntimeException
}
}
修正方案:
@Transactional(rollbackFor = Exception.class)
public void updateData() throws Exception {
// ...
}
③ 非public方法
@Transactional
protected void internalProcess() {
// 事务不生效!
}
④ 多线程断联
@Transactional
public void batchProcess() {
new Thread(() -> {
// 新线程中的操作不在事务中
updateInventory();
}).start();
}
⑤ 特殊方法过滤
@Transactional
public final void finalMethod() {
// final方法事务失效
}
⑥ 错误捕获
@Transactional
public void process() {
try {
// 数据库操作
} catch (Exception e) {
// 捕获异常但未抛出
log.error("错误", e);
}
}
正确做法:
catch (Exception e) {
throw new RuntimeException(e);
}
4. 传播机制组合效果表
| 主事务 | 子事务 | 效果 |
|---|---|---|
| REQUIRED | REQUIRED | 共用事务,任一失败全回滚 |
| REQUIRED | REQUIRES_NEW | 子事务独立,主事务失败不影响子事务 |
| REQUIRED | NESTED | 子事务可部分回滚,主事务失败强制回滚所有 |
| REQUIRED | NOT_SUPPORTED | 子事务暂停主事务,非事务执行 |
| NEVER | REQUIRED | 直接抛出异常,拒绝执行 |
5. 调试技巧
- 开启事务日志:
logging.level.org.springframework.jdbc=DEBUG
logging.level.org.springframework.transaction=TRACE
- 检查代理类型:
@SpringBootApplication
@EnableTransactionManagement(proxyTargetClass = true)
public class Application { ... }
- 使用事务模板:
TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
transactionTemplate.execute(status -> {
// 事务操作
return null;
});