【问题标题】:Spring Boot + JPA + Exception HandlingSpring Boot + JPA + 异常处理
【发布时间】:2016-02-01 20:35:34
【问题描述】:

我正在进行概念验证,以将我们的项目转换为 Spring Boot 应用程序。我有一个带有 2 种方法的存储库类:保存和查找。

@Repository
public class UserDataRepo {
    private EntityManager em;

    public boolean save(UserDataModel model) {
        try {
            UserDataModel existingModel = find(model.getTable(), model.getFieldName();
            model.setId(existingModel.getId());
            this.em.merge(model);
            this.em.flush();
            return false;
        } catch (NoResultException e) {
            this.em.persist(model);
            this.em.flush();
            return true;
        }
    }

    public UserDataModel find(String table, String field) {
        Query query = this.em.createQuery(FIND_USERDATA_STATEMENT);
        query.setParameter("table", table);
        query.setParameter("fieldName", field);
        return (UserDataModel) query.getSingleResult(); // throws NoResultException
    } 
}

在我的 Spring Boot Application 类中,我添加了@EnableJpaRepositories@EnableTransactionManagement。我的应用程序启动正常,没有任何错误。但是正如你所看到的,save 方法依赖于 find 方法来确定是合并还是持久化。如果没有记录,find 方法抛出 NoResultException。我观察到的是它永远不会落入 save 方法的 catch 块中。 Spring Boot 只是抛出一个错误,说 NoResultException。

在合并的情况下,它就像一个魅力。所以这意味着实体管理器工作正常。

我不知道还需要配置什么。有什么想法吗?

从日志中添加错误:

org.springframework.dao.EmptyResultDataAccessException: No entity found for query; nested exception is javax.persistence.NoResultException: No entity found for query
at org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:389)
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:223)
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:417)
at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:59)
at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:213)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:147)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)

【问题讨论】:

  • 看起来您要捕获的异常是 org.springframework.dao.EmptyResultDataAccessException 而不是 NoResultExceptionNoResultException 嵌套在外部异常中,因此您的 try { } catch 块不会看到它
  • @MrWiggles 我将其更改为捕获 EmptyResultDataAccessException,但它仍然不属于 catch 块。使用 Spring Boot 时应该以不同方式映射异常还是使用 Spring Boot 配置持久性的方式?
  • 您可以暂时将您的 catch 块更改为 Throwable 以查看实际引发的异常是什么。
  • @MrWiggles 我试过了,实际的异常是一样的:javax.persistence.NoResultException: No entity found for query。给出一个上下文,现有的 Web 应用程序是 spring+jersey+hibernate,它在 tomcat 中部署为战争。逻辑工作正常。异常在 save 内的 catch 块中被捕获。我正在研究这个 POC,以展示是否可以将其捆绑为阴影 jar 并将其配置为 Spring Boot 应用程序。除了异常不会传播到父块的逻辑之外,每个微服务似乎都可以工作。
  • @MrWiggles 我刚刚在 find 方法中添加了“throws NoResultException”,当我使用 Spring Boot 启动应用程序时它就起作用了。不过很奇怪。

标签: java spring hibernate jpa spring-boot


【解决方案1】:

我不会推荐这种行为。不建议像这样使用getSingleResult()。阅读更多关于为什么never to use getSingleResult in JPA here 的信息。而是尝试这样的查询。

    Query query = this.em.createQuery(FIND_USERDATA_STATEMENT);
    query.setParameter("table", table);
    query.setParameter("fieldName", field);

    List results = query.getResultList();
    if (results.isEmpty()) {
        return null;
    }
    else if (results.size() == 1)  {
        return results.get(0);
    }
    throw new NonUniqueResultException();

【讨论】:

  • 我同意你和你提到的文章。由于这是一个生产代码,还需要进行其他重构。但是为什么 Spring Boot 无法将异常向上级联/翻译并落在 catch 块内?
  • NoResultException 是运行时异常,您必须在 find(...) 方法中捕获它。
【解决方案2】:

对于像这样的简单持久性案例,我强烈建议查看 Spring Data JPA,它可以让您避免执行任何复杂的手动持久性管理。如果这对您不起作用,我强烈建议您不要使用 Exceptions for Logic。也就是说,我不是依靠失败的 Find 查询中的异常来确定对象是否需要持久化或合并,而是首先使用计数进行查询以确定对象是否存在,然后根据该结果继续合并或坚持。一般来说,在业务逻辑中使用异常被认为是一种反模式,请参阅:When is it OK to use exception handling for business logic?

【讨论】:

  • 感谢您的建议。需要进行大量重构:)
【解决方案3】:

我刚刚在 find 方法中添加了“throws NoResultException”并且它起作用了。不知道为什么在 Spring Boot 中配置应用程序时必须专门将其添加到签名中。

【讨论】:

    猜你喜欢
    • 2023-03-28
    • 2015-02-23
    • 2016-09-29
    • 2022-11-22
    • 2020-08-03
    • 1970-01-01
    • 2022-01-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多