java - 在@Transactional 服务方法中捕获 DataIntegrityViolationException

标签 java spring hibernate transactions

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

  • Controller 处理传入请求,调用服务方法
  • 服务方式为@Transactional , 做一些业务逻辑并调用 Persistence 方法
  • 持久性方法由 DAO 对象处理,将内容保存到数据库中。

  • 数据库有一个 uniqueusername 的约束用户的。我现在的工作方式是这样的:
  • 客户端向 Controller 发送请求
  • Controller 调用服务
  • 服务尝试通过 DAO 保存对象。如 DataViolationException发生,服务返回自定义异常
  • Controller 捕获自定义异常并发回适当的响应


  • 伪代码是这样的:
    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 必须处理持久性异常,我不喜欢那样。

    我更喜欢服务抛出“可理解”的异常以供 Controller 处理。该服务知道预期什么持久性异常以及何时发生,并且能够“翻译”广泛的 DataIntegrityViolationException变成一个有意义的。

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

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

    最佳答案

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

    import org.springframework.data.jpa.repository.JpaRepository;
    
    public interface UserDAO extends JpaRepository<User, Long> {
    }
    
    服务:
     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);
                }
            }
        }
    

    关于java - 在@Transactional 服务方法中捕获 DataIntegrityViolationException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36498327/

    相关文章:

    java - 如何发送列表的值而不是引用(其中列表是 map 的值)?

    java - 通过 Intent 的 Android 共享不起作用,

    java - 如何在没有 sun.security 的情况下使用 key 对创建 X509Certificate

    spring - 在 Spring 中使用相同的作业详细信息动态重新安排 CronTriggerBean

    java - 如何强制 Spring 使用事务执行读查询

    java - 无法解决 Maven 中的 httpComponent 依赖关系

    java - 对递归方法和循环感到困惑

    java - Spring 中对 List 或 Map 的响应主体

    hibernate - 如何在Grails中的hasMany关系中指定查询?

    java - 一对一映射的概念。解释映射