Spring事务嵌套引发的问题

事务回滚

构建必要的代码如下:

//UserController.java
@GetMapping("/users")
public List<User> queryAll() {
  return userApplication.findAll();
}
 
//UserApplication.java
@Service
@Transactional
public class UserApplication {
 
  @Autowired
  private UserService userService;
  @Autowired
  private UserRepository userRepository;
 
  public List<User> findAll() {
    try {
      userService.query("hresh2");
    } catch (Exception e) {
    }
 
    return userRepository.findAll();
  }
}
 
//UserServiceImpl.java
@Override
@Transactional
public UserResponse query(String name) {
  if (!name.equals("hresh")) {
    throw new IllegalArgumentException("name is forbidden");
  }
  return null;
}
 
public void validateName(String name) {
  if (!name.equals("hresh")) {
    throw new IllegalArgumentException("name is forbidden");
  }
}

我们利用 postman 来进行测试,发现报错结果和预期不大一样:

rollback-only异常令我对事务有了新的认识

关键信息变为了 Transaction silently rolled back because it has been marked as rollback-only,这里我们暂不讨论错误提示信息为何发生了改变,先集中讨论报错原因。

根据基础知识中介绍的@Transactional 的作用范围和传播机制可知,当我们在 Service 文件类上添加 @Transactional 时,该注解对该类中所有的 public 方法都生效,且传播机制默认为 PROPAGATION_REQUIRED,即如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。

在这种情况下,外层事务(UserApplication)和内层事务(UserServiceImpl)就是一个事务,任何一个出现异常,都会在 findAll()执行完毕后回滚。如果内层事务抛出异常 IllegalArgumentException(没有catch,继续向外层抛出),在内层事务结束时,Spring 会把内层事务标记为“rollback-only”;这时外层事务发现了异常 IllegalArgumentException,如果外层事务 catch了异常并处理掉,那么外层事务A的方法会继续执行代码,直到外层事务也结束时,这时外层事务想 commit,因为正常结束没有向外抛异常,但是内外层事务是同一个事务,事务已经被内层方法标记为“rollback-only”,需要回滚,无法 commit,这时 Spring 就会抛出org.springframework.transaction.UnexpectedRollbackException: Transaction silently rolled back because it has been marked as rollback-only,意思是“事务静默回滚,因为它已被标记为仅回滚”。

具体分析详见:https://www.hreshhao.com/transaction-rollback-and-invalidation/#title-8

解决方案:https://www.dounaite.com/article/62554f8eae87fd3f795bc4bf.html




posted on 2023-08-08 15:52  HHHuskie  阅读(114)  评论(0编辑  收藏  举报

导航