news 2026/9/4 5:23:25

Spring事务管理之事务传播机制的使用

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Spring事务管理之事务传播机制的使用

一、事务传播机制核心判断思路

1. 三个问题

  • 外层调用方有没有事务?
  • 内层失败的时候,要不要影响外层事务?(内层抛异常,外层要不要回滚)
  • 内层是否需要独立提交 / 回滚?还是只是子事务?

2. 判断流程图

3. 具体传播机制使用规则

1) REQUIRED(默认)
  • 规则:有就加入外层事务;没有自己新建。
  • 场景:serviceA 开启事务调用 serviceB,A、B 在同一个事务。B 抛异常,A 一起回滚;A 抛异常 B 也回滚。
  • 适合:大部分新增修改业务(CRUD),绝大多数情况不用手动改传播属性。
2) SUPPORTS
  • 规则:如果外层有事务就用外层事务;没有事务就直接非事务运行。
  • 适合:select 查询方法,不需要强制事务。
3) MANDATORY
  • 禁止外层无事务调用,防止漏开事务。一般框架内部使用,业务代码很少写。
  • 场景:内部核心更新方法,要求调用者必须开启事务,否则抛IllegalTransactionStateException。
  • 适用:内层一定要跑在事务里面,外层没事务直接报错
4) REQUIRES_NEW
  • 规则:调用 B 的时候,挂起 A 的事务,开启 B 全新独立事务;B 提交 / 回滚完全独立;B 结束,恢复 A 的挂起事务。
  • 适用:内层需要独立事务,内层失败不影响外层
  • 典型业务场景:操作日志、审计日志、错误记录。主业务失败回滚,但是错误日志必须入库保存,不能跟着业务一起回滚。
  • 坑:如果不 catch,内层抛出异常直接抛给外层,外层也会回滚!内层独立事务回滚了,外层代码必须捕获内层异常。
5) NOT_SUPPORTED
  • 规则:如果外层有事务,先挂起外层事务,本方法非事务执行。方法不能运行在事务中
  • 场景:大批量数据导入,大批量查询,不想占用数据库事务连接,避免长事务。
6) NEVER
  • 规则:禁止外层存在事务,如果外层有事务直接抛异常。
  • 适用:业务几乎不用,用于校验。
7) NESTED(JDBC savepoint 保存点)
  • 规则:嵌套事务。
    • 内层回滚:只回滚到 savepoint,外层事务还可以继续提交;
    • 外层回滚:整个全部回滚,内层提交也跟着回滚。
  • 适合场景:一个大业务,分多个子步骤;子步骤可以单独回滚,但外层整体失败全部回滚。
  • 限制:只支持 JDBC 事务;不支持 JTA 分布式事务。MySQL InnoDB 支持 savepoint。
  • 和 REQUIRES_NEW 区别:不是新开独立事务,是外层事务里面设置保存点 savepoint
  • REQUIRES_NEW vs NESTED:
    • REQUIRES_NEW:完全独立两个事务,内层提交不受外层控制;外层回滚,内层已经提交的数据不受影响。
    • NESTED:属于同一个物理事务,设置保存点;外层一旦回滚,嵌套部分全部回滚。

二、事务使用场景

事务的核心原则:

  • 事务用来保证一组数据库操作「要么全部成功,要么全部失败」(ACID);
  • 单条 SQL 天然是事务,不需要额外加 @Transactional

注:MySQL InnoDB:每一条 DML(insert/update/delete)本身就是一个自动提交的独立事务。

1.需要事务的场景

业务需要多条数据库操作,逻辑上必须原子绑定,一部分成功一部分失败是错误数据。

1) 多写操作,业务要求原子性

- 例:转账:A 扣钱、B 加钱;扣钱成功,加钱失败,数据不能出现 A 钱少了 B 没多。
- 例:订单业务:创建订单、扣库存、生成流水、扣余额;任意一步失败,全部回滚。
- 例:主表 + 子表同时保存:保存订单 + 保存订单明细;不能订单存成功,明细丢失。

2) 读 + 写组合,需要数据一致性

先查询,再根据查询结果做更新;过程中不允许其他线程修改这条数据。
例:扣库存:先查库存 > 0,再扣减。不加事务会出现**超卖**。
注意:

  • 单纯 select 不会加锁;
  • 需要锁要配合@Transactional+ 悲观锁 / select ... for update或者乐观锁版本号
3)业务失败必须回滚中间全部变更

调用多个 Mapper/DAO,中间抛出异常,前面已经入库的数据必须撤销。

4) 需要控制隔离级别、避免脏读 / 不可重复读

业务对读取数据一致性要求高,需要指定隔离级别。

5) 需要用到传播行为特性

比如日志需要独立事务 REQUIRES_NEW;嵌套业务需要 NESTED保存点

2.不需要事务的场景

1) 只有单条 SQL(insert /update/delete)
// 单条更新,数据库本身自动事务,不需要 @Transactional userMapper.updateById(user);

就算不加注解,这条语句要么成功要么失败,不会半截执行。再加事务属于多余开销,开启事务、提交都有性能损耗

误区:所有 service 方法统一加 @Transactional,造成不必要事务开启,连接占用时间变长,高并发下加剧数据库锁、长事务问题。

2) 纯查询方法,没有任何写操作
  • 普通列表查询、详情查询,只读,没有新增修改删除。
  • 不要随便加@Transactional(readOnly = true),不是不能加,但不是必须。

补充:

  • readOnly=true作用:给底层数据库驱动一个 hint,部分驱动会做优化;不会锁表,不能防止数据被别的线程修改。
  • 如果只是简单查询,直接去掉注解,性能更好。
3) 业务允许部分成功部分失败

批量导入,允许一部分成功、一部分失败,失败记录单独记录日志,不希望全部回滚。

这种场景反而不能加大事务;捕获单条异常,跳过错误,继续处理下一条。

4) 纯内存逻辑,完全不操作数据库

方法只有参数校验、调用 RPC、调用 http、内存计算,没有 DB 操作,加事务毫无意义

3. 灰色场景(容易踩坑)

1) 方法里面有 DB 操作,也有 RPC/HTTP 调用
@Transactional public void createOrder(){ orderMapper.insert(order); // 远程调用,网络超时卡住 remotePayApi.call(); }

RPC 网络 IO 放在事务内部 → 长事务大坑。数据库连接长期不释放,锁持有时间变长,数据库压力暴涨。
正确:把远程调用移出事务边界,事务尽量小,只包裹数据库操作。

2) 循环批量处理
// 错误:大事务,全部在一个事务,数据量大会回滚日志暴涨,锁时间长 @Transactional public void batchSave(List<data> list){ for(Data d : list){ mapper.insert(d); } }
  • 如果要求全部成功全部失败:保留事务。
  • 如果允许部分失败:去掉外层大事务,分批提交,捕获异常。

三、经典业务场景

1.下单主业务失败,希望错误日志一定要落库,日志不能回滚?
👉 REQUIRES_NEW,内层异常必须 try‑catch。
2.一个大事务,里面某个子步骤失败只回滚子步骤,外层可以继续提交;外层失败全部回滚?
👉 NESTED 嵌套事务
3.查询接口,外层有事务就参与事务,没有事务就直接查询?
👉 SUPPORTS
4.内部更新方法,强制调用者必须开启事务,否则直接报错,防止漏事务?
👉 MANDATORY
5.批量导入几万条数据,不想长事务,外层有事务要挂起?
👉 NOT_SUPPORTED

四、坑点

1. 传播机制生效前提:方法必须是外部调用,不能本类内部调用
A 类的 a () 方法调用本类的 b (),b 上 @Transactional 传播机制完全失效,因为不走 AOP 代理对象。要注入自己或者拆分成不同 Service。
2.REQUIRES_NEW 一定要捕获内层异常
如果 REQUIRES_NEW 方法抛出异常没有 catch,异常向上抛,外层事务依然会标记回滚。内层事务已经回滚,外层也回滚。
3. NESTED 底层是 JDBC savepoint,Mybatis+MySQL InnoDB 可用;分布式 JTA 环境不支持。
4.REQUIRES_NEW 会挂起事务,占用数据库连接;大量使用会消耗连接池,不要滥用。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/4 5:23:10

基于向量数据库与NLP的智能笔记系统:从信息孤岛到知识图谱

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/4 5:22:47

意图经济落地:从一句话到可执行行程的意图解析链路

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/4 5:22:28

技术视频制作全流程:从脚本撰写、FFmpeg剪辑到发布涨粉实战

嗨&#xff0c;各位 CSDN 的读者朋友&#xff0c;好久不见。最近收到不少同学私信问我&#xff1a;平时写技术博客的人&#xff0c;到底是怎么把那些干货内容变成“最新视频”的&#xff1f;为什么有些博主发一条内容就能收获关注&#xff0c;而自己更新却没几个人看&#xff1…

作者头像 李华
网站建设 2026/9/4 5:21:32

从零构建高并发电竞赛事平台后端:Spring Boot与WebSocket实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/4 5:21:27

多系统引导(Multiboot):Linux 与 Windows 共存

多系统引导&#xff08;Multiboot&#xff09;&#xff1a;Linux 与 Windows 共存 本篇是前面《UEFI 双盘双系统&#xff08;双Windows、双 Linux、Windows Linux&#xff09;》的外篇——具体实操及细节示例。 本文实操示例&#xff1a;Ubuntu 22.04 与 Windows 10 双系统搭建…

作者头像 李华
网站建设 2026/9/4 5:21:17

第十三讲:点亮LED

大家好&#xff0c;接下来的一段时间我将开始学习野火的Linux系统课程并将学习到的干货逐步更新到我的CSDN博客中。没时间刷课的同学可以把我的博客喂给AI突击一下连接好电源线USB1./sys/class讲解/sys是sysfs 虚拟文件系统&#xff08;内存里&#xff0c;重启消失&#xff09;…

作者头像 李华