1. 确保事务配置生效
@Transactional 生效原则 1
@Transactional 注解只有标记在 public 方法上才能生效。
原因:Spring 默认事务通过动态代理的方式实现 AOP 对目标方法进行增强,因此只能对 public 方法进行增强。private 方法无法代理到,Spring 自然也无法动态增强事务处理逻辑。
@Transactional 生效原则 2
必须通过代理过的类(Spring 注入的 Bean)从外部调用 @Transactional 注解标记的方法,事务才能生效。
原因:Spring 通过 AOP 技术对方法进行增强,要调用增强过的方法必然是调用代理后的对象。
2. 使用恰当的异常处理
通过 AOP 实现事务处理可以理解为,使用 try…catch… 包裹了标记 @Transactional 注解的方法,当方法出现了异常并且满足一定条件的时候,在 catch 里面可以设置事务回滚,没有异常则直接提交事务。
上述的“一定条件”,主要包括以下两点:
-
只有异常传播出了标记了 @Transactional 注解的方法,事务才能回滚。
如果在事务方法中捕获了异常,则需要通过手动编码处理事务回滚:
@Transactional public void createUserRight1(String name) { try { userRepository.save(new UserEntity(name)); throw new RuntimeException("error"); } catch (Exception ex) { log.error("create user failed", ex); // 手动处理事务回滚 TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); } } -
否则,默认设置为只有出现 RuntimeException(非受检异常)或 Error 时,事务才会回滚。
如果希望 Spring 针对其他异常也可以回滚,则可以相应配置 @Transactional 注解的 rollbackFor 或 noRollbackFor 属性来覆盖其默认设置:
// 遇到所有的 Exception 都回滚事务 @Transactional(rollbackFor = Exception.class) public void createUserRight2(String name) throws IOException { // IOException 是受检异常 userRepository.save(new UserEntity(name)); otherTask(); }
3. 复杂事务中细化配置事务传播方式
复杂事务场景:不在涉及多次数据库操作的业务逻辑中,希望将它们作为独立的事务进行提交或回滚。让主事务中包含独立的子事务,子事务的失败不会使得主事务回滚、不影响主流程。
细化配置事务传播方式:使用 @Transactional 注解的 Propagation 属性。为注解加上 propagation = Propagation.REQUIRES_NEW 来设置 REQUIRES_NEW 方式的事务传播策略,含义是执行到这个方法时需要开启新的事务,并挂起当前事务。
主事务方法:
@Transactional
public void createUserRight(UserEntity entity) {
createMainUser(entity);
try{
subUserService.createSubUserWithExceptionRight(entity);
} catch (Exception ex) {
// 捕获异常,防止主方法回滚
log.error("create sub user error:{}", ex.getMessage());
}
}
子事务方法:
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void createSubUserWithExceptionRight(UserEntity entity) {
log.info("createSubUserWithExceptionRight start");
userRepository.save(entity);
throw new RuntimeException("invalid status");
}