日常开发优先用声明式事务(@Transactional),因其无侵入、配置简单、自动管理事务生命周期;仅在需细粒度控制事务边界、动态决定提交/回滚或部分逻辑独立事务时,才选用编程式事务。

直接说结论:日常开发优先用声明式事务(@Transactional),只在需要精细控制事务边界、动态决定是否提交、或嵌套逻辑中部分操作需独立事务时,才考虑编程式事务。
声明式事务怎么用
核心就是加注解,Spring 自动帮你管好事务的开启、提交和回滚。
- 加在 service 层的 public 方法上(不能加在 private 或 final 方法上)
- 默认只对 RuntimeException 和 Error 回滚,若想对普通 Exception 也回滚,要写
rollbackFor = Exception.class - 事务传播行为常用
REQUIRED(默认),嵌套调用时复用已有事务;需要新事务就用REQUIRES_NEW - 读操作建议加
readOnly = true,可提升性能并避免意外修改 - 注意:事务失效常见原因包括——方法非 public、自调用(this.method())、异常被 try-catch 吞掉没抛出、代理未生效(如没走 Spring 容器管理的对象)
编程式事务怎么用
适合需要手动干预事务流程的场景,主流方式是用 TransactionTemplate。
- 注入
TransactionTemplate,调用execute()方法包裹业务代码 - 在 lambda 内写业务逻辑,成功自动提交;抛异常或调用
status.setRollbackOnly()则回滚 - 可精确控制哪几行 DB 操作在事务里,比如“先查再判断,满足条件才更新”,事务只包后面那部分
- 也可用
PlatformTransactionManager+TransactionDefinition手动获取TransactionStatus,但更繁琐,一般推荐TransactionTemplate
什么时候该选哪一种
关键看事务控制粒度和灵活性需求。
立即学习“Java免费学习笔记(深入)”;
- 整个方法要么全成功、要么全失败 → 声明式事务足够,干净又可靠
- 一个方法里有多个数据库操作,但只想给其中一部分加事务(比如前两步不进事务,后三步必须原子)→ 编程式事务
- 根据运行时参数决定是否开启事务(如开关配置、用户角色)→ 编程式事务
- 需要在事务中途捕获异常、记录日志、再决定是否回滚 → 编程式事务
- 跨数据源或复杂分布式事务协调 → 两者都不够,得上 Saga、Seata 等方案
实际用法对比示例
比如处理订单:扣库存 + 创建订单 + 发消息。发消息不应在事务内(否则事务回滚会导致消息发不出或重复发),但前三步要强一致。
- 声明式事务会把整个方法包进事务,发消息也跟着回滚或重试,容易出问题
- 编程式事务可只把“扣库存”和“创建订单”放进
transactionTemplate.execute(),发消息放在外面,彻底解耦



















