一、前言
事务管理是企业级应用开发中的核心需求,Spring框架提供了强大而灵活的事务管理支持。然而在实际开发中,事务失效问题却频频出现,常常导致数据不一致却又难以排查。今天我们就来讲一讲事务失效的常见场景,让大家避免踩坑。
二、非public方法导致事务失效
@Service
public class OrderService {
@Transactional
void createOrder(Order order) { // 非public方法
// 订单创建逻辑
orderDao.save(order);
// 其他业务操作
}
}
Spring的事务AOP代理默认使用JDK动态代理(基于接口)或CGLIB代理(基于类)。对于非public方法:
1、 JDK动态代理只能代理接口中的方法
2、 CGLIB虽然可以代理类方法,但无法代理非public方法
3、 Spring默认会跳过非public方法的@Transactional
注解
三、自调用
@Service
public class UserService {
public void updateUser(User user) {
// 其他业务逻辑
this.updateUserStatus(user); // 自调用
}
@Transactional
public void updateUserStatus(User user) {
// 更新用户状态
userDao.updateStatus(user);
}
}
Spring事务基于AOP代理实现,自调用时绕过代理直接调用目标方法,导致事务注解失效。
三、异常类型不匹配或被捕获导致回滚失败
@Service
public class PaymentService {
@Transactional
public void processPayment(Payment payment) {
try {
paymentDao.save(payment);
// 支付逻辑
} catch (Exception e) {
log.error("支付失败", e);
throw new BusinessException("支付处理失败"); // 非RuntimeException
}
}
}
Spring事务默认只在抛出RuntimeException和Error时回滚,检查型异常(checked exception)不会触发回滚。异常被捕获场景
@Service
public class InventoryService {
@Transactional
public void deductStock(Long productId, int quantity) {
try {
inventoryDao.reduceStock(productId, quantity);
// 其他业务逻辑
} catch (Exception e) {
log.error("扣减库存失败", e); // 捕获异常但未抛出
}
}
}
事务拦截器只有在收到异常时才会触发回滚逻辑,如果异常被捕获且未重新抛出,事务将正常提交。
四、数据库引擎不支持事务MySQL的MyISAM引擎不支持事务,InnoDB才支持。如果表使用了不支持事务的存储引擎,Spring事务将失效。
五、 多数据源配置不当导致事务失效Spring事务管理器通常只管理一个数据源的事务,直接操作其他数据源将不受事务管理。
六、方法final或static导致事务失效
1、 final方法无法被CGLIB代理(基于继承实现)
2、 static方法属于类而非实例,AOP无法拦截
七、事务超时设置不合理
@Service
public class DataProcessService {
@Transactional(timeout = 1) // 1秒超时
public void processLargeData() {
// 处理大量数据,耗时超过1秒
dataDao.processBatch1();
dataDao.processBatch2();
// ...
}
}
事务超时后会自动回滚,如果业务处理时间超过超时设置,会导致事务意外回滚。