Spring 事务传播行为有什么用?
- 标签:后端、Spring
🏛 业务架构师
事务传播行为的用处,本质是回答一个业务问题:"我这个方法和调用我的方法,应该共享同一个一致性边界,还是各自独立?" 它把"事务边界如何组合"从编程劳动变成声明式配置。
三个我实战中真实受益的场景:
- 独立留痕:支付主流程失败回滚,但风控记录、失败日志必须独立提交——
REQUIRES_NEW让"主事务回滚、审计事务提交"两个矛盾需求共存。没有传播行为,就得手工跨库或消息表,复杂度翻倍; - 批量容错:批量导入 1000 条数据,第 37 条违反业务规则,期望"回滚这一条、其余继续"——
NESTED的保存点机制正好表达这种"部分回滚"语义; - 统一入口约束:核心资金操作标
MANDATORY,谁直接调用(忘了开事务)立刻抛异常——把"必须在事务内执行"的架构约束固化成运行时检查,比文档约定可靠得多。
架构师视角的提醒:传播行为是双刃剑。REQUIRES_NEW 会额外占用连接池连接,嵌套事务在不同数据库/ORM 支持度不一;团队里如果人人随手指定传播行为,事务边界会变成谜团。我的治理方式:传播行为的选择写进代码评审 checklist,核心服务的入口方法明确声明 REQUIRED,特殊语义必须注释业务理由。声明式事务的价值在于让一致性边界可见、可审计,前提是有人对它做治理。
🏗 系统架构师
一句话:在一个调用链里声明「哪些操作共享一个原子边界、哪些自成边界」。
三个我经历过的真实用法:
- 审计与主业务解耦:操作日志用 REQUIRES_NEW——主事务回滚,日志仍要留下「谁在什么时候尝试了什么」,合规场景的硬需求;
- 批量容错:导入一万条数据,外层 REQUIRED、每条 NESTED:单条失败回滚到保存点、记录错误继续跑,最后提交成功的部分——既不全成也不全败;
- 强制契约:核心资金方法标 MANDATORY,禁止任何人脱离事务调用,把架构约束固化成运行时校验。
反向价值(识别坏味道):
- REQUIRES_NEW 滥用等于把一个大原子拆成多个小原子,中间态对外暴露——外层回滚时内层已提交,出现「订单没了、库存扣了」的对账事故;
- 传播行为救不了跨库跨服务:两个数据源两个事务管理器,传播行为不跨连接。这种边界要么 XA,要么业务侧补偿。
💻 全栈开发专家
它的价值是把"事务边界"变成可编程的决策。我举三个真实场景:
1. 操作日志必须留痕:下单主流程回滚了,但"用户尝试下单失败"的审计记录要保留——日志服务用 REQUIRES_NEW 独立提交,主事务回滚不影响它。
2. 批量处理部分失败:导入 1000 条数据,单条失败只回滚这一条、整体继续。每条处理用 NESTED(savepoint),失败回滚到保存点,外层 catch 后继续,最后统一提交成功的部分。
3. 强约束防御:核心资金方法标 MANDATORY,谁脱离事务直接调用(比如定时任务忘了开事务),一调用就抛异常,把 bug 暴露在测试阶段而不是对账时。
反面教材也处理过:不理解传播行为,Service 互相调用全默认 REQUIRED,一个校验失败把不该回滚的上游数据也滚没了。排查这类问题我看三点:propagation 属性值、调用是否跨代理(自调用失效)、异常是否被 try-catch 吞掉(默认只回滚 RuntimeException,且切面必须能看到异常)。一句话:传播行为是"事务的组合逻辑",设计好了能兼顾一致性和可用性。