Spring的事务什么情况下会失效
ps:如果这篇帖子对于还在找工作和找实习的你有所帮助,可以关注我,给本贴点赞、评论、收藏并订阅专栏;同时不要吝啬您的花花
Spring事务管理基于AOP动态代理实现,只有通过代理对象调用事务方法时,@Transactional注解才会生效;一旦绕过代理、配置出错、异常处理不当或底层环境不支持,事务就会直接失效。下文梳理开发中所有高频失效场景,兼顾原理讲解与实战修复。
一、AOP代理机制失效(最常见,占80%以上场景)
Spring默认仅对代理对象的public方法生成事务切面,若破坏代理规则,注解会被直接忽略,事务完全不生效。
1. 事务方法非public修饰(private/protected/default)
失效根源:Spring AOP代理仅拦截public方法,非public方法的@Transactional注解会被事务管理器直接跳过,不会生成事务切面。
失效案例:
@Service
public class OrderService {
// 非public方法,事务失效
@Transactional
private void createOrder() {
// 数据库操作
}
}
修复方案:
方案1:改为public修饰(最简修复)
@Service
public class OrderService {
// 改为public,代理可正常拦截事务
@Transactional
public void createOrder() {
orderMapper.insertOrder(order);
}
}
方案2:抽取事务方法到独立public接口实现(架构规范)
public interface OrderOperateService {
void createOrder();
}
@Service
public class OrderOperateServiceImpl implements OrderOperateService {
@Transactional
@Override
public void createOrder() {
orderMapper.insertOrder(order);
}
}
2. 同类内部方法自调用(this调用)
失效根源:同类内部方法调用用的是原始对象(this),而非Spring代理对象,直接绕过事务切面,事务不生效。这是开发中最易踩坑的场景。
失效案例逐场景判定+原因解析:
案例1:非事务方法 + this调用事务方法
@Service
public class OrderService {
// 非事务方法调用同类事务方法,this调用绕过代理
public void batchCreate() {
this.createOrder();
}
@Transactional
public void createOrder() {
// 数据库操作
}
}
结论:事务完全失效;原因:this指向原始对象而非代理对象,直接跳过事务切面,@Transactional注解不生效。
案例2:非事务方法 + 省略this调用事务方法
@Service
public class OrderService {
public void batchCreate() {
createOrder();
}
@Transactional
public void createOrder() {
// 数据库操作
}
}
结论:事务完全失效;原因:省略this本质仍是当前对象(原始对象)调用,和案例1底层逻辑一致,未走代理拦截。
案例3:事务方法 + 省略this调用事务方法
@Service
public class OrderService {
@Transactional
public void batchCreate() {
createOrder();
}
@Transactional
public void createOrder() {
// 数据库操作
}
}
结论:事务失效;原因:即使外层方法开启事务,内部方法依旧是原始对象自调用,不触发代理切面,内层事务不生效,仅外层事务生效。
案例4:事务方法 + this调用事务方法
@Service
public class OrderService {
@Transactional
public void batchCreate() {
this.createOrder();
}
@Transactional
public void createOrder() {
// 数据库操作
}
}
结论:事务失效;原因:典型同类自调用坑位,this绕过代理,内层事务切面不执行,事务传播机制失效。
修复方案(附完整Java代码):
方案1:注入自身代理对象调用(推荐)
核心思路:通过@Autowired注入自身Bean(代理对象),用代理对象调用目标事务方法,触发切面。
@Service
public class OrderService {
// 注入自身代理对象
@Autowired
private OrderService orderService;
// 外层方法(可加@Transactional,按需配置)
public void batchCreate() {
// 代理对象调用,事务生效
orderService.createOrder();
}
@Transactional
public void createOrder() {
// 数据库增删改操作
}
}
方案2:拆分方法到不同类(解耦彻底)
核心思路:将事务方法抽离到独立Service,跨类调用自动走代理,彻底避免自调用问题。
// 事务逻辑独立Service
@Service
public class OrderCreateService {
@Transactional
public void createOrder() {
// 数据库操作
}
}
// 调用方Service
@Service
public class OrderBatchService {
@Autowired
private OrderCreateService orderCreateService;
public void batchCreate() {
// 跨类调用,代理生效,事务正常执行
orderCreateService.createOrder();
}
}
3. 事务方法被final/static修饰
失效根源:JDK动态代理基于接口实现,CGLIB代理基于继承实现;final/static方法无法被重写,代理无法生成事务增强逻辑。
修复方案:
方案1:移除final/static关键字(直接修复)
方案2:封装为非静态实例方法(规范写法)
二、@Transactional注解配置错误
注解本身配置或加载异常,会导致事务规则不生效,属于显性配置失误。
1. 事务传播行为配置不当
失效根源:若配置Propagation.NOT_SUPPORTED(不支持事务)、Propagation.NEVER(禁用事务),当前方法会强制脱离事务,即使外层有事务也会挂起/报错。
失效案例:@Transactional(propagation = Propagation.NOT_SUPPORTED) 修饰的方法,无事务生效。
失效代码:
@Service
public class OrderService {
// 配置不支持事务的传播级别,事务强制失效
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void createOrder() {
orderMapper.insertOrder(order);
}
}
修复方案+代码:
方案1:使用默认REQUIRED传播级别(通用场景)
@Service
public class OrderService {
// 默认REQUIRED,存在事务则加入,不存在则新建
@Transactional
public void createOrder() {
orderMapper.insertOrder(order);
}
}
方案2:使用REQUIRES_NEW新建独立事务(子事务隔离)
@Service
public class OrderService {
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void createOrder() {
orderMapper.insertOrder(order);
}
}
2. 类未被Spring容器管理
失效根源:@Transactional仅对Spring Bean生效,手动new创建的对象、未加@Service/@Component等注解的类,不会被代理,事务失效。
失效代码:
// 未加Spring注解,不属于容器Bean,事务失效
public class OrderService {
@Transactional
public void createOrder() {
orderMapper.insertOrder(order);
}
}
// 调用处:手动new对象,不走代理
public class TestController {
public void test() {
OrderService orderService = new OrderService();
orderService.createOrder();
}
}
修复方案+代码:
方案1:添加@Service注解并依赖注入
@Service
public class OrderService {
@Transactional
public void createOrder() {
orderMapper.insertOrder(order);
}
}
@RestController
public class TestController {
// 注入Spring代理对象
@Autowired
private OrderService orderService;
public void test() {
orderService.createOrder();
}
}
方案2:使用@Component注解(通用组件)
@Component
public class OrderManager {
@Transactional
public void createOrder() {
orderMapper.insertOrder(order);
}
}
3. 注解被覆盖/优先级冲突
失效根源:类级别和方法级别同时加@Transactional,方法级注解会覆盖类级;若自定义切面优先级高于事务切面,会提前捕获异常导致事务回滚失效。
修复方案:统一注解粒度,调整事务切面优先级为最高(@Order(Ordered.LOWEST_PRECEDENCE))。
三、异常处理逻辑缺陷(事务不回滚≠不生效,属于回滚失效)
Spring默认仅对RuntimeException和Error回滚事务,异常处理不当会导致事务不回滚,看似失效。
1. 手动捕获异常未重新抛出
失效根源:事务切面只有捕获到未处理的异常才会触发回滚;若try-catch吞掉异常,事务管理器感知不到异常,不会回滚。
失效案例:
@Transactional
public void createOrder() {
try {
// 数据库异常操作
} catch (Exception e) {
// 吞掉异常,事务不回滚
log.error("异常", e);
}
}
失效代码(异常被吞,事务不回滚):
@Transactional
public void createOrder() {
try {
orderMapper.insertOrder(order);
// 模拟异常
int i = 1 / 0;
} catch (Exception e) {
// 吞掉异常,事务管理器无感知,不回滚
log.error("订单创建异常", e);
}
}
修复方案+代码:
方案1:捕获后重新抛出运行时异常
@Transactional
public void createOrder() {
try {
orderMapper.insertOrder(order);
int i = 1 / 0;
} catch (Exception e) {
log.error("订单创建异常", e);
// 重新抛出,触发事务回滚
throw new RuntimeException("订单创建失败");
}
}
方案2:手动标记事务回滚
@Transactional
public void createOrder() {
try {
orderMapper.insertOrder(order);
int i = 1 / 0;
} catch (Exception e) {
log.error("订单创建异常", e);
// 手动强制回滚当前事务
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
}
2. 抛出受检异常未指定rollbackFor
失效根源:Spring默认不回滚受检异常(如IOException、SQLException),直接抛出此类异常事务不会回滚。
失效代码(受检异常不回滚):
@Service
public class OrderService {
// 默认不回滚受检异常,抛出IOException事务不回滚
@Transactional
public void createOrder() throws IOException {
orderMapper.insertOrder(order);
throw new IOException("文件读写异常");
}
}
修复方案+代码:
方案1:指定rollbackFor覆盖所有异常
@Service
public class OrderService {
// 受检/运行时异常全部回滚
@Transactional(rollbackFor = Exception.class)
public void createOrder() throws IOException {
orderMapper.insertOrder(order);
throw new IOException("文件读写异常");
}
}
方案2:精准指定受检异常回滚
@Service
public class OrderService {
// 仅针对IOException回滚
@Transactional(rollbackFor = IOException.class)
public void createOrder() throws IOException {
orderMapper.insertOrder(order);
throw new IOException("文件读写异常");
}
}
3. 异常被外层切面提前捕获
失效根源:自定义日志/监控切面优先级高于事务切面,提前捕获异常并处理,事务切面无法感知,回滚失效。
修复方案:降低自定义切面优先级,保证事务切面优先拦截异常。
四、底层环境与数据库限制
1. 数据库引擎不支持事务
失效根源:MySQL的MyISAM引擎无事务特性,即使Spring事务生效,数据库层也无法执行回滚;InnoDB引擎才支持事务。
失效场景说明:建表时指定MyISAM引擎,无事务能力
-- 失效建表语句(MyISAM不支持事务) CREATE TABLE `order` ( `id` bigint NOT NULL AUTO_INCREMENT, `order_no` varchar(32) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
修复方案+代码:
方案1:修改表引擎为InnoDB
ALTER TABLE `order` ENGINE=InnoDB;
方案2:重新建表指定InnoDB
CREATE TABLE `order` ( `id` bigint NOT NULL AUTO_INCREMENT, `order_no` varchar(32) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
2. 未配置事务管理器
失效根源:传统Spring项目需手动配置DataSourceTransactionManager,Spring Boot虽自动配置,但多数据源场景若未指定事务管理器,事务会失效。
修复方案:单数据源无需配置,多数据源手动绑定对应事务管理器。
五、特殊业务场景失效
1. 多线程/异步方法调用(@Async)
失效根源:Spring事务绑定线程本地变量(ThreadLocal),异步方法会开启新线程,新线程无事务上下文,事务独立且外层无法控制回滚。
失效代码(异步线程事务脱离):
@Service
public class OrderService {
@Autowired
private OrderMapper orderMapper;
@Transactional
public void createOrder() {
orderMapper.insertOrder(order);
// 异步线程,无事务上下文,外层无法控制回滚
new Thread(() -> {
orderMapper.updateStock();
// 此处异常,外层事务不回滚
int i = 1 / 0;
}).start();
}
}
修复方案+代码:
方案1:异步方法单独加@Transactional
@Service
public class OrderService {
@Autowired
private OrderMapper orderMapper;
@Transactional
public void createOrder() {
orderMapper.insertOrder(order);
asyncUpdateStock();
}
// 异步方法独立开启事务
@Async
@Transactional
public void asyncUpdateStock() {
orderMapper.updateStock();
int i = 1 / 0;
}
}
方案2:同步执行避免线程隔离
@Service
public class OrderService {
@Autowired
private OrderMapper orderMapper;
@Transactional
public void createOrder() {
// 同步执行,共用当前事务
orderMapper.insertOrder(order);
orderMapper.updateStock();
int i = 1 / 0;
}
}
2. 分布式/跨服务调用
失效根源:Spring事务是本地事务,仅作用于单个JVM、单个数据源;跨服务、跨库调用属于分布式场景,本地事务无法跨进程/跨库生效。
修复方案:引入分布式事务方案(Seata、TCC、可靠消息最终一致性)。
3. 事务超时/只读属性冲突
失效根源:配置readOnly = true的事务方法执行写操作,会抛出异常且事务不生效;超时时间设置过短,事务未执行完就被强制提交/回滚。
修复方案:只读事务仅做查询,写操作关闭readOnly;合理设置timeout时长。
核心避坑总结:事务方法必用public、禁止同类this调用、不吞异常、指定rollbackFor、数据库用InnoDB、多线程/分布式单独处理事务。
ps:如果这篇帖子对于还在找工作和找实习的你有所帮助,可以关注我,给本贴点赞、评论、收藏并订阅专栏;同时不要吝啬您的花花
本专栏聚焦Spring全生态体系,从IoC/AOP核心原理入手,覆盖Spring Boot自动配置、事务管理、Web开发等实战内容。拆解循环依赖、动态代理等高频面试难点,助力开发者从入门到精通,打通单体到微服务的技术链路,解决企业级开发痛点,提升架构设计与问题排查能力,成为Java后端进阶的必备技术专栏。