1. 自调用问题
场景:在同一个类中,一个非事务方法直接调用内部的事务方法。
@Service
public class OrderService {
public void createOrder() {
// 其他逻辑...
this.updateStatus(); // 自调用,事务失效!
}
@Transactional
public void updateStatus() {
// 数据库更新操作
}
}原因:this.updateStatus() 是目标对象内部调用,没有经过代理对象,因此事务切面无法介入。
解决方案:
注入自身代理
@Service
public class OrderService {
// @Autowired
// public OrderService self;
@Autowired
private ApplicationContext context;
public void createOrder() {
// 其他逻辑...
// 使用自身代理对象调用updateStatus()
/// self.updateStatus();
OrderService proxy = context.getBean(OrderService.class);
proxy.updateStatus();
}
@Transactional
public void updateStatus() {
// 数据库更新操作
}
}重构代码,将
updateStatus()拆分到另一个Service中。
2. 异常处理不当
场景:事务方法抛出的异常没有被事务配置捕获,或异常被“吃掉”。
@Transactional
public void process() {
try {
// 数据库操作
throw new RuntimeException("错误");
} catch (Exception e) {
// 捕获后未抛出,事务无法回滚
log.error("出错", e);
}
}
// 或:抛出的是受检异常(如IOException),但默认只对RuntimeException回滚解决方案:
确保异常抛出:在catch块中根据需要抛出
RuntimeException或使用TransactionAspectSupport.currentTransactionStatus().setRollbackOnly()手动回滚。明确指定回滚异常:
@Transactional(rollbackFor = Exception.class)。
3. 方法修饰符非public
场景:Spring的@Transactional 默认只对public方法生效。如果用在protected、private或包级可见方法上,事务不生效。
解决方案:将方法改为public。如果必须非public,需结合更复杂的AOP配置(如AspectJ模式),但通常不推荐。
4. 数据库或表引擎不支持事务
场景:使用MySQL时,如果表引擎是MyISAM(不支持事务),那么事务注解完全无效。
解决方案:确保数据库支持事务(如MySQL的InnoDB引擎)。建表时指定:CREATE TABLE (...) ENGINE=InnoDB;。
5. 传播行为设置不当
场景:对传播行为的误解导致预期外的回滚或提交。
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void innerMethod() {
// 如果此方法被一个已有事务的方法调用,
// 但调用方捕获了它的异常,可能不影响外部事务
}解决方案:深入理解每种传播行为(如REQUIRED、REQUIRES_NEW、NESTED)的语义,并根据业务逻辑正确选择。
6. 数据源/事务管理器配置问题
场景:在多数据源情况下,未指定事务管理器或指定错误。
@Transactional // 默认使用primary事务管理器,若操作非primary数据源则失效
public void multiDataSourceOp() {
// 操作第二个数据源
}解决方案:明确指定事务管理器:
@Transactional("secondTransactionManager")7. 多线程环境下事务上下文丢失
场景:在新线程中执行数据库操作,原事务上下文不会自动传播。
@Transactional
public void parentMethod() {
new Thread(() -> {
// 此处操作不在事务内!
someRepository.save(...);
}).start();
}解决方案:避免在线程中执行事务性操作。如需异步事务,考虑使用复杂的分布式事务方案(如Seata)或可靠消息队列。
🔧 问题排查清单
遇到事务失效,可按此顺序快速排查:
检查调用方式:是否自调用?
检查方法可见性:是否为
public?检查异常类型:是否被捕获或非
RuntimeException?是否正确配置rollbackFor?检查数据库引擎:是否为InnoDB等支持事务的引擎?
检查传播行为:是否符合预期?
检查数据源配置:多数据源时是否指定正确的事务管理器?
检查线程环境:是否在新线程/线程池中调用?
💎 核心原则总结
理解事务失效,关键在于抓住一点:Spring事务是通过代理实现的。任何导致代理逻辑无法被执行或拦截的情况,都可能让事务失效。在编码时,优先使用声明式事务(@Transactional),并遵循上述规范;对于复杂场景,可考虑使用 TransactionTemplate 进行编程式事务管理,它提供更细粒度的控制。