在@Transactional服务方法中捕获DataIntegrityViolationException

时间:2016-04-08 11:20:54

标签: java spring hibernate transactions

我有一个带有Hibernate的REST Spring启动应用程序。为简单起见,我们假设这个工作流程:

  1. 控制器处理传入请求,调用服务方法
  2. 服务方法是@Transactional,执行一些业务逻辑并调用持久性方法
  3. 持久性方法由DAO对象处理,将内容保存到数据库中。
  4. 数据库对用户的uniqueusername约束。我现在的工作方式是:

    1. 客户端向Controller发送请求
    2. 控制器呼叫服务
    3. 服务尝试通过DAO保存对象。如果发生DataViolationException,则Service返回自定义异常
    4. Controller捕获自定义异常并发回适当的响应
    5. 伪代码是:

      public class UserController {
          @RequestMapping("/user")
          public User createUser(...){
              try{
                  return userService.createUser(...);
              } catch (UserAlreadyExistsException e){
                  // Do some processing and return error message to client
              }
          }
      }
      
      public class UserService {
          @Transactional
          public User createUser(...){
              (...)
              try{
                  userDAO.save(newUserObject);
              } catch(DataIntegrityViolationException e){
                  throw new UserAlreadyExistsException(username);
              }
          }
      }
      

      但是,这样我在尝试创建重复用户时会收到错误。

      javax.persistence.RollbackException: Transaction marked as rollbackOnly
      

      解决此问题的一种方法似乎是让DataIntegrityViolationException“冒泡”从事务中解决(而不是在服务中捕获它)。但这意味着Controller必须处理持久性异常,我不喜欢这样。

      我更喜欢服务为控制器提出“可理解的”例外情况。该服务知道期望的持久性异常以及何时能够将广泛的DataIntegrityViolationException“翻译”为有意义的异常。

      有没有办法以这种方式处理异常?我并不特别喜欢使用“2层服务层”来实现这一目标。

      编辑:我想抛出自定义异常的另一个原因是编译器需要捕获它。我想强制执行控制器来处理可能发生的所有可能的异常。

2 个答案:

答案 0 :(得分:0)

使用

注释您的服务方法
content://com.miui.gallery.open/raw/%2Fstorage%2Femulated%2F0%2FDCIM%2FCamera%2FIMG_20190310_123752.jpg

如果抛出异常,它将告诉spring不提交事务,这样您就可以在控制器中捕获它

答案 1 :(得分:0)

您的存储库需要扩展JpaRepository,并且在执行此操作时。您可以从该存储库使用saveAndFlush方法。这意味着,您的代码将立即在数据库上执行,并且异常将在完成事务之前引发,并且您可以在Catch块中捕获它。

public class UserService {
    @Transactional
    public User createUser(...){
        (...)
        try{
            userDAO.saveAndFlush(newUserObject);
        } catch(DataIntegrityViolationException e){
            throw new UserAlreadyExistsException(username);
        }
    }
}