被误解的 Spring 事务:它不是不存在,而是你没注意

张开发
2026/4/10 23:12:27 15 分钟阅读

分享文章

被误解的 Spring 事务:它不是不存在,而是你没注意
写在前面“我做了几个单体项目和微服务项目感觉很少遇到 Spring 事务。好像 Spring 里面并不存在事务只有在微服务项目中才有分布式事务。”这是我收到的一条读者留言也是很多初学者的共同困惑。因为感觉不到所以认为不存在因为没主动配置所以觉得它可有可无。这是一个危险的认知误区。实际上Spring 事务几乎是每一个涉及数据库写操作的 Java 后端项目都在默默使用的功能。你之所以“没感觉到”恰恰是因为 Spring 把它做得太优雅、太透明了——你只需要一个Transactional注解剩下的它全包了。今天我们就来彻底拆解 Spring 事务它到底是什么和 MySQL 事务有什么区别为什么会失效以及你该怎么用好它。一、Spring 事务 ≠ 数据库事务它是“管家”不是“工人”先澄清一个核心概念MySQL 事务是数据库层面的能力通过BEGIN、COMMIT、ROLLBACK来控制一组 SQL 要么全部成功要么全部失败。Spring 事务是基于 AOP 对数据库事务的封装和管理它帮你自动开启、提交或回滚事务让你不用手写事务模板代码。换句话说Spring 本身没有创造事务它只是让使用数据库事务变得更简单。// 没有 Spring 事务时你需要手动管理 public void transfer() { Connection conn null; try { conn dataSource.getConnection(); conn.setAutoCommit(false); // 执行 SQL conn.commit(); } catch (Exception e) { conn.rollback(); } finally { conn.close(); } } // 有了 Spring 事务你只需要这样 Transactional public void transfer() { // 执行 SQLSpring 自动管理提交/回滚 }所以Spring 事务是存在的而且一直在用。你之所以没感觉是因为 Spring Boot 自动配置 默认行为让你“无感使用”了。二、Spring 事务的核心原理AOP 代理 数据库事务Spring 事务的实现原理可以用一张图概括关键点Spring 事务依赖 AOP 代理。如果代理对象调不到方法比如内部调用事务就会失效。三、Spring 事务的 7 种传播行为propagation这是 Spring 事务独有的概念MySQL 事务没有。它定义了事务方法被另一个事务方法调用时如何传播。四、Spring 事务失效的 8 大经典场景附解决方案这是面试和工作中的高频痛点。场景1方法没有被 Spring 代理调用内部调用Service public class UserService { public void saveUser() { this.doInsert(); // ❌ 内部调用Transactional 失效 } Transactional public void doInsert() { // 插入数据库 } }解决方案注入自身代理或拆分 Service。Service public class UserService { Autowired private UserService selfProxy; public void saveUser() { selfProxy.doInsert(); // ✅ 通过代理调用 } }场景2方法不是 public 的Transactional只对public方法生效。protected、private 会被忽略。场景3异常被 catch 吞掉了Transactional public void update() { try { // 抛异常 } catch (Exception e) { // 什么都没做事务不会回滚 } }解决方案要么重新抛出异常要么手动回滚TransactionAspectSupport.currentTransactionStatus().setRollbackOnly()。场景4抛出的异常类型不对Spring 事务默认只回滚RuntimeException和ErrorException不会回滚。Transactional public void update() throws Exception { throw new Exception(checked exception); // ❌ 不会回滚 }解决方案Transactional(rollbackFor Exception.class)场景5数据库引擎不支持事务MySQL 中 MyISAM 引擎不支持事务需要改为 InnoDB。Spring 事务无能为力。场景6多数据源配置错误多个数据源时如果没有指定事务管理器可能用错了 DataSource。场景7方法被 final 或 static 修饰AOP 代理无法覆盖 final 方法。场景8Spring 代理方式错误使用 CGLIB 代理时如果目标类没有实现接口且代理类无法继承比如 final 类事务也会失效。一张图总结事务失效场景五、Spring 事务 vs MySQL 事务区别一目了然核心结论Spring 事务是对 MySQL 事务的“增强版遥控器”它让你更方便地使用数据库事务但无法超越数据库事务的物理边界。六、开发中什么时候需要 Spring 事务实际场景场景1单体项目中的写操作用户下单扣库存 创建订单 减余额银行转账A 账户减钱B 账户加钱文章发布保存文章 更新用户统计 推送消息这些操作如果不用事务可能出现库存扣了订单没生成、钱扣了对方没收到等严重数据不一致。场景2多表关联更新更新主表同时更新子表、日志表、关联表。场景3需要保证最终一致性的批量操作批量导入数据时要么全部成功要么全部失败。场景4只读事务优化查询方法上加Transactional(readOnly true)数据库会优化查询性能Spring 也不会再检测 dirty 状态。Transactional(readOnly true) public ListUser findAll() { return userMapper.selectList(); }你之所以“感觉不到”是因为很多业务方法默认就在事务中Spring Boot 默认没有为每个方法开启但你的 Service 方法可能被调用的上层有事务或者你依赖的框架自带事务。七、澄清误区Spring 事务 ≠ 分布式事务你提到的“微服务项目中才有分布式事务”这个理解部分正确但需要区分Spring 事务也叫本地事务解决的是单个数据库连接内的一致性比如一个方法里操作了多张表它们都在同一个数据库。分布式事务解决的是跨多个微服务、多个数据库的一致性比如订单服务 库存服务 积分服务。Spring 事务在微服务项目中依然存在且常用每个微服务内部自己的数据库操作依然需要 Spring 事务保证本地一致性。跨服务的分布式事务则需要额外的方案TCC、SAGA、Seata 等Spring 事务不能解决跨服务问题。所以不是说微服务项目里 Spring 事务就不用了而是每个服务内部仍然在用只是服务之间需要更强的协调机制。八、最佳实践用好 Spring 事务的 5 条建议始终在 public 方法上使用Transactional并且从外部调用通过代理。明确指定回滚异常Transactional(rollbackFor Exception.class)避免 checked 异常不回滚。合理设置传播行为默认 REQUIRED 够用但独立日志操作可用 REQUIRES_NEW。查询方法加上readOnly true提升性能且语义清晰。避免在事务中做耗时操作如远程调用、大量循环计算缩短事务持有时间。总结事务是你默默守护数据的“隐形守护者”回到最初的问题Spring 事务真的存在吗不仅存在而且你每天都在用——只是它太安静、太自动化以至于你忽略了它的存在。Spring 事务就像代码世界的“隐形守护者”在你执行写操作时默默保证数据要么全部成功要么全部回滚。Spring 事务与 MySQL 事务的关系MySQL 提供基础能力Spring 提供优雅的使用方式。Spring 事务与分布式事务的关系本地事务解决单库问题分布式事务解决跨库问题二者不是替代而是互补。希望这篇文章能帮你彻底理清 Spring 事务的本质下次面试官问“事务失效的场景”时你不仅能说出 8 条还能讲清楚背后的原理。

更多文章