# 事务传播行为对比表解析
# 1. 什么是"当前有事务"
意思是:在调用这个方法的时候,已经有一个事务在运行了
@Transactional
public void 主方法() {
// 这里已经开始了一个事务
子方法(); // 调用子方法时,"当前有事务" ✅
}
public void 子方法() {
// 当被主方法调用时,这里"当前有事务"
}
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
# 2. 什么是**"当前无事务"**
意思是:在调用这个方法的时候,没有事务在运行
public void 普通方法() {
// 这里没有 @Transactional 注解
子方法(); // 调用子方法时,"当前无事务" ✅
}
1
2
3
4
5
2
3
4
5
# 3. "典型场景"
这个就不用多说:
意思就是:这种传播行为最适合用在什么情况下。
# 重新解释的表格(用生活例子)
| 传播行为 | 当前有事务(有人排队) | 当前无事务(没人排队) | 你该怎么行动? | 生活例子 |
|---|---|---|---|---|
| REQUIRED (需要) | 插队进去 (加入别人的队) | 自己开个新队(新创建一个事务) | 最常用,大家都这样做 | 食堂打饭:有队就排,没队就开新队 |
| REQUIRES_NEW (必须新) | 到旁边开个新队 (让别人先等着) | 自己开个新队 | 独立的事,不受别人影响 | 买票时突然要上厕所:先离队去厕所,回来重新排队 |
| NESTED (嵌套) | 在队伍里做个标记 (可以单独退回标记点) | 自己开个新队 | 大任务里的小任务 失败不影响整体 | 买套餐:主餐+饮料+甜点 饮料没了只退饮料钱 |
| SUPPORTS (支持) | 跟着排队 | 不排队了 直接办事 | 可排队可不排队的事 | 问路:如果别人在排队就问下,没排队就直接问 |
| NOT_SUPPORTED (不支持) | 不排队了 让队伍等着 | 不排队 | 长时间的事 别占着队伍 | 办护照要1小时:让其他人先办,你单独处理 |
| NEVER (绝不允许) | 大喊:不行! (抛异常) | 不排队 | 绝不能排队的事 | 火灾逃生:绝不能排队!排队就危险! |
| MANDATORY (强制) | 跟着排队 | 大喊:不行! (抛异常) | 必须排队的事 | 银行取大额现金:必须排队办理,不能插队 |
# 场景1:REQUIRED(最常用)
@Service
public class 订单服务 {
@Transactional // 默认就是 REQUIRED
public void 创建订单() {
// 开始排队(新建事务)
保存订单();
扣库存(); // 也在同一个队伍里
发短信(); // 也在同一个队伍里
// 一起提交或一起回滚
}
}
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# 场景2:REQUIRES_NEW(独立的事)
@Service
public class 日志服务 {
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void 记录日志() {
// 不管别人在不在排队,我都要新开一个队
// 我自己的事办完就提交,不受别人影响
}
}
@Transactional
public void 下订单() {
保存订单(); // 主队伍
// 日志必须独立保存,就算订单失败也要记录
日志服务.记录日志(); // 开新队伍
// 如果这里出错,订单回滚,但日志已经保存
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 场景3:NESTED(套餐里的单品)
@Transactional
public void 批量导入员工() {
for (员工 : 员工列表) {
try {
导入单个员工(员工); // NESTED:套餐里的单品
} catch (Exception e) {
// 这个人导入失败,继续导入下一个
// 只回滚这个人的操作
}
}
// 所有人导入成功才一起提交
}
@Transactional(propagation = Propagation.NESTED)
public void 导入单个员工(员工) {
// 如果失败,只回滚这个员工的操作
// 不影响其他人
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 场景4:SUPPORTS(看情况)
@Transactional(propagation = Propagation.SUPPORTS)
public 报表 生成报表() {
// 生成报表(查询操作)
// 如果别人在事务中调用我,我就用他的事务
// 如果别人不在事务中调用,我就不用事务
// 适合:查询接口,可事务可非事务
}
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# 场景5:NOT_SUPPORTED(别占着队伍)
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void 导出Excel() {
// 导出10万条数据到Excel(耗时5分钟)
// 如果别人有事务,先挂起,让我先办
// 办完了再恢复别人的事务
// 适合:长时间操作,避免锁表太久
}
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# 场景6:NEVER(严禁排队)
@Transactional(propagation = Propagation.NEVER)
public void 健康检查() {
// 检查数据库连接是否正常
// 绝对不能在有事务时调用!
// 否则抛异常:IllegalTransactionStateException
// 适合:监控、健康检查
}
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# 场景7:MANDATORY(必须排队)
@Transactional(propagation = Propagation.MANDATORY)
public void 审计记录() {
// 记录谁在什么时候做了什么
// 必须和业务操作在同一个事务中!
// 如果没事务就抛异常
// 适合:审计日志,必须原子性
}
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# 简单选择指南
问自己几个问题:
- 这个方法需要事务吗?
- 需要 → 用
REQUIRED(默认) - 不需要 → 考虑
NOT_SUPPORTED或NEVER - 看情况 →
SUPPORTS
- 需要 → 用
- 这个方法失败时,要不要影响调用者?
- 不要影响 →
REQUIRES_NEW - 可以部分影响 →
NESTED(如果数据库支持) - 必须一起 →
REQUIRED
- 不要影响 →
- 这个方法必须在事务中/外执行吗?
- 必须在事务中 →
MANDATORY - 必须在事务外 →
NEVER
- 必须在事务中 →
# 一句话总结
- REQUIRED:随大流,有队就排,没队自开(90%的情况)
- REQUIRES_NEW:我的事重要,开新队优先办
- NESTED:大任务里的小步骤,失败不影响整体
- SUPPORTS:可有可无,你们决定
- NOT_SUPPORTED:别烦我,我要单独办
- NEVER:谁让我排队我跟谁急!
- MANDATORY:不排队就不给办!
现在明白了吗?主要是理解"当前有没有事务"就是指调用这个方法的时候,调用者自己是不是已经在事务中了。