在@Transactional 服务方法中捕获 DataIntegrityViolationException

Mar*_*lka 11 java spring hibernate transactions

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

  1. 控制器处理传入的请求,调用服务方法
  2. 服务方法是@Transactional,做一些业务逻辑并调用持久化方法
  3. 持久性方法由 DAO 对象处理,将内容保存到数据库中。

数据库uniqueusername用户有约束。我现在的工作方式是这样的:

  1. 客户端向控制器发送请求
  2. 控制器调用服务
  3. 服务尝试通过 DAO 保存对象。如果DataViolationException发生,服务返回自定义异常
  4. 控制器捕获自定义异常并发回适当的响应

伪代码是这样的:

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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

javax.persistence.RollbackException: Transaction marked as rollbackOnly
Run Code Online (Sandbox Code Playgroud)

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

我更喜欢服务抛出“可理解”的异常以供控制器处理。该服务知道预期什么持久性异常以及何时发生,并且能够将广泛的“转换”DataIntegrityViolationException为有意义的异常。

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


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

mfl*_*zak 5

您的存储库需要扩展 JpaRepository,当您这样做时。您可以使用该存储库中的 saveAndFlush 方法。这意味着,您的代码将立即在数据库上执行,并在完成事务之前抛出异常,您将能够在 Catch 块中捕获它。我还添加了用于删除操作的示例。

存储库:

import org.springframework.data.jpa.repository.JpaRepository;

public interface UserDAO extends JpaRepository<User, Long> {
}
Run Code Online (Sandbox Code Playgroud)

服务:

 public class UserService {
        private UserDAO userDAO;

        (...)

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

        @Transactional
        public void deleteUser(...){
            (...)
            try{
                userDAO.delete(deletingUserObject);
                userDAO.flush();
            } catch(DataIntegrityViolationException e){
                throw new UserException(username);
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)


bla*_*ird 2

注释你的服务方法

@Transactional(rollbackFor = UserAlreadyExistsException.class)
Run Code Online (Sandbox Code Playgroud)

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

  • “理解”哪个约束被打破的最佳方式是什么,例如,如果一个实体有多个约束?或对不同实体的约束? (3认同)