LarryDpk
发布于 2025-05-10 / 5 阅读
0

Spring 事务管理

Spring 事务管理

一句话核心:Spring 用 AOP 将数据库连接与业务方法绑定在 同一线程,把“开始–提交/回滚”的模板逻辑交给 PlatformTransactionManager,开发者只需通过 @Transactional 描述业务边界。


1. 事务管理方式

方式 典型用法 适合场景 优劣
声明式(推荐)
@Transactional
在类/方法上加注解 绝大多数业务方法 语义清晰、与业务解耦,易统一管控
编程式
TransactionTemplate
transactionTemplate.execute(cb) 需要围绕 部分代码块 做事务、或按运行时条件决定事务属性 API 简单,但仍破坏业务代码整洁
底层 API
PlatformTransactionManager
getTransaction→commit/rollback 框架/中间件自研、对事务粒度完全掌控 代码冗长,易漏提交/回滚,更常用于封装模板

2. 核心配置与属性

属性 默认 说明
传播行为 Propagation REQUIRED 调用方有事务→加入,没有则新建
隔离级别 Isolation DEFAULT(由驱动决定,一般 = DB 默认) 防脏读/不可重复读/幻读;应与 DB 事务隔离级别对应
超时 Timeout -1(无限) 单位秒,超过则抛 TransactionTimedOutException 回滚
只读 ReadOnly false 表示逻辑上只读,部分驱动可做优化;若执行写操作会被 DB 拒绝(取决于驱动)
回滚规则 RollbackFor RuntimeException, Error 可指定 rollbackFor = Exception.class 覆盖;noRollbackFor 反向指定

易错点@Transactional 只有 外部调用 经过 Spring 代理时才生效;同类内部方法互调无法触发事务拦截(需拆分 Service 或注入代理自身)。


3. 传播行为速查表

Propagation 行为描述 常见用例
REQUIRED 有则加入,无则新建 默认;大多数业务场景
REQUIRES_NEW 挂起外层事务,永远新建 记录操作日志、发送消息等子事务
NESTED 若存在事务则开启 保存点,可局部回滚 多步扣减库存(回滚子步骤)
NOT_SUPPORTED 非事务 运行,挂起外层事务 查询大表避免长事务锁
SUPPORTS 有事务就加入,无事务就非事务 读场景兼容
MANDATORY 必须在事务中调用,否则异常 DAO 层硬性要求
NEVER 调用方不能有事务 只读报表专用

4. 典型代码范式

4.1 Service 层声明式事务

@Service  
@RequiredArgsConstructor  
public class OrderService {  
  
 private final OrderRepository repo;  
 private final PaymentService paymentService;  
  
  @Transactional(rollbackFor = Exception.class)  
 public void createOrder(OrderDTO dto) {  
 Order order = repo.save(convert(dto));        // ① 写订单  
  paymentService.pay(order);                    // ② 调远程  
 // ③ 其它逻辑  
 }}  
  • 传播:默认 REQUIREDpay() 若遇异常将回滚整个事务。
  • 回滚:显式 rollbackFor = Exception.class,防止业务抛 受检异常 时 Spring 不回滚。

4.2 编程式—模板控制

@TransactionalService  
public class ReportJob {  
  
 private final TransactionTemplate tx;  
  
 public void generate() {  
  tx.executeWithoutResult(status -> {  
 try { doWriteHeavyQuery();  // 可能超时  
 } catch (Exception ex) { status.setRollbackOnly();              // 手动回滚  
 log.error("生成报表失败", ex);  
 } }); }}  

4.3 多数据源 ChainedTransactionManager

@Bean  
public PlatformTransactionManager chainedTx(  
 DataSourceTransactionManager dsTx, JpaTransactionManager      jpaTx) { return new ChainedTransactionManager(dsTx, jpaTx);}  

应用场景:单个业务方法需同时写关系表和 NoSQL(Mongo 等),要求原子性。Spring 通过依次提交/回滚多个 TxM “伪两段” 解决,仍非强一致,生产建议升级为 分布式事务/最终一致 方案(如 Seata、TCC、SAGA)。


5. 与分布式事务的衔接

思路 方案 适用
本地事务 + 可靠消息 Spring 事务内插入消息表,提交后异步发送 电商下单、异步库存扣减
Seata AT / TCC Spring Boot + Seata Starter 多库 ACID,要在同局域网
Saga(状态机) Spring Cloud Alibaba Saga / Akka 长事务、跨微服务编排
两阶段提交 XA Atomikos, Narayana 对性能敏感度低、需要强一致

6. 常见坑与最佳实践 ✅

  1. 同类内部调用失效
  • 解决:将方法抽到新 Bean;或通过 AopContext.currentProxy() 获取代理再调用。
  1. 受检异常不回滚
  • 添加 rollbackFor = Exception.class 或把业务异常继承 RuntimeException
  1. Long Tx 锁表
  • 拆分读写、使用 PROPAGATION_REQUIRES_NEW 子事务,或按乐观锁重试。
  1. 批量写性能差
  • 关闭自动提交,分批 flush;或用 JDBC batch。
  1. 事务传播 + 异常捕获
  • 不要在外层吞掉异常,否则 Spring 判断不到失败 ➜ 无回滚;捕获后务必 throwsetRollbackOnly()
  1. 只读事务滥用
  • readOnly=true 仅在某些驱动生效;若混用写操作会抛错。
  1. 多线程不共享事务
  • @Async、CompletableFuture 会脱离当前线程,无事务;需要在父线程先提交或采用 TransactionAware 设计。
  1. 连接泄漏
  • 必须使用 Spring 数据访问模板或 @Transactional 让框架接管,自己 getConnection() 要确保 finally 关闭。

7. Checklist for Production

  • 将事务边界放在 Service 层,Controller 尽量无事务。
  • 所有新增的 DAO 方法确保在事务内调用。
  • 定期用 p6spy / slow query 审计长事务 & 慢 SQL。
  • 监控连接池活跃连接数,防重叠长事务耗尽连接。
  • 对高并发写入表开启 乐观锁 + 重试,减少死锁。
  • 核心业务使用 幂等设计 + 补偿机制,降低分布式回滚复杂度。

总结:Spring 事务本质是 AOP + ThreadLocal 封装模板,把海量重复的连接与回滚逻辑隐藏,让开发只关注业务边界。掌握 @Transactional 的属性组合、传播语义以及常见陷阱,即可在单体或微服务架构中编写高可靠的数据库操作。