引言
在 Spring 应用开发里,事务管理是保障数据一致性的关键功能。但在实际编码过程中,因各种不当操作,常常会出现事务失效的情况。我们将围绕异常捕获处理、抛出检查异常以及非 public 方法这三个常见场景,深入探讨事务失效的原因和对应的解决办法。
一、异常捕获处理后未抛出导致事务失效
1. 问题描述
在使用 Spring 事务时,如果在事务方法里手动捕获了异常,却没有重新抛出,Spring 就无法感知到异常,进而不会触发事务回滚。
com.example.service.OrderService.java
@Service
public class OrderService {
@Autowired
private OrderRepository orderRepository;
@Transactional
public void createOrder() {
try {
orderRepository.save(new Order());
// 模拟异常
int result = 1 / 0;
} catch (Exception e) {
// 捕获异常但未抛出
e.printStackTrace();
}
}
}
在上述代码中,createOrder 方法捕获了异常并进行了简单处理,没有重新抛出,即便发生异常,事务也不会回滚。
2. 解决方案
手动将捕获的异常重新抛出,让 Spring 能够感知到异常从而触发事务回滚。
com.example.service.OrderService.java
@Service
public class OrderService {
@Autowired
private OrderRepository orderRepository;
@Transactional
public void createOrder() throws Exception {
try {
orderRepository.save(new Order());
// 模拟异常
int result = 1 / 0;
} catch (Exception e) {
// 手动抛出异常
throw e;
}
}
}
二、抛出检查异常导致事务失效
1. 问题描述
Spring 默认只对 RuntimeException 及其子类和 Error 进行事务回滚。若抛出的是检查异常(即 Exception 的子类,但不是 RuntimeException 及其子类),事务不会回滚。
com.example.service.UserService.java
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
@Transactional
public void saveUser() throws Exception {
userRepository.save(new User());
// 抛出检查异常
throw new Exception("Test Exception");
}
}
2. 解决方案
在 @Transactional 注解中通过 rollbackFor 属性明确指定需要回滚的异常类型,将其设置为 Exception.class,这样所有异常都能触发事务回滚。
com.example.service.UserService.java
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
@Transactional(rollbackFor = Exception.class)
public void saveUser() throws Exception {
userRepository.save(new User());
// 抛出检查异常
throw new Exception("Test Exception");
}
}
三、非 public 方法导致事务失效
1. 问题描述
Spring 的事务管理是基于 AOP 代理实现的,而 AOP 代理只会对 public 方法进行增强。若将 @Transactional 注解加在非 public 方法上,事务会失效。
com.example.service.ProductService.java
@Service
public class ProductService {
@Autowired
private ProductRepository productRepository;
@Transactional
private void updateProduct() {
productRepository.updateStock();
// 模拟异常
throw new RuntimeException("Test Exception");
}
}
2. 解决方案
把方法的访问权限修改为 public,让 Spring AOP 代理能够对其进行事务增强。
com.example.service.ProductService.java
@Service
public class ProductService {
@Autowired
private ProductRepository productRepository;
@Transactional
public void updateProduct() {
productRepository.updateStock();
// 模拟异常
throw new RuntimeException("Test Exception");
}
}
总结
Spring 事务失效在异常捕获处理、抛出检查异常以及非 public 方法这三个场景下较为常见。开发时,捕获异常后要记得手动抛出,对检查异常需配置 rollbackFor 属性,同时要保证事务方法为 public 访问权限。