【问题标题】:JpaRepository throwing EntityNotFoundException instead of returning nullJpaRepository 抛出 EntityNotFoundException 而不是返回 null
【发布时间】:2019-03-03 18:10:15
【问题描述】:

我正面临这个非常不寻常的错误。 我有这个JpaRepository<SomeObject, Long>

public interface SomeRepository extends JpaRepository<SomeObject, Long> {
      @Query("select someObject from SomeObject someObject where someObject.id = ?1")
     public SomeObject getSomeObject(int id);
}

它工作正常,当我尝试获取 ID 不存在的 SomeObject 时,它只是返回 null 并且我处理它并继续进行。

但是,当我引入了我的应用程序的多个实例(例如 2 个)并将它们隐藏在负载均衡器后面时。我执行此操作(作为脚本),它会在其中重复检索/创建/删除 SomeObject。

当我只有一个实例时,我运行以下脚本:检索(返回 null)-> 创建 SomeObject,删除 SomeObject 并重复检索(返回 null)-> ...等

一切正常,如预期的那样^

在我的多实例设置中,负载均衡器可互换地将请求重新路由到实例。 意思是,操作现在按以下顺序发生:

实例(1)检索(返回null)

实例(2)SomeObject的创建

实例(1)删除SomeObject

在下一次迭代中,观察到一些奇怪的行为!

Instance(2)检索(这里不是返回null,Spring一下子抛出如下异常):

Caused by: org.springframework.orm.jpa.JpaObjectRetrievalFailureException: Unable to find somePackage.SomeObject with id 1; nested exception is javax.persistence.EntityNotFoundException: Unable to find somePackage.SomeObject with id 1
 at org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:389) ~[spring-orm-4.3.8.RELEASE.jar!/:4.3.8.RELEASE]
 at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:246) ~[spring-orm-4.3.8.RELEASE.jar!/:4.3.8.RELEASE]
 at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:488) ~[spring-orm-4.3.8.RELEASE.jar!/:4.3.8.RELEASE]
 at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:59) ~[spring-tx-4.3.8.RELEASE.jar!/:4.3.8.RELEASE]
 at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:213) ~[spring-tx-4.3.8.RELEASE.jar!/:4.3.8.RELEASE]
 at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:147) ~[spring-tx-4.3.8.RELEASE.jar!/:4.3.8.RELEASE]
 at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.3.8.RELEASE.jar!/:4.3.8.RELEASE]
 at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:133) ~[spring-data-jpa-1.11.3.RELEASE.jar!/:?]
 at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.3.8.RELEASE.jar!/:4.3.8.RELEASE]
 at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92) ~[spring-aop-4.3.8.RELEASE.jar!/:4.3.8.RELEASE]
 at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.3.8.RELEASE.jar!/:4.3.8.RELEASE]
 at org.springframework.data.repository.core.support.SurroundingTransactionDetectorMethodInterceptor.invoke(SurroundingTransactionDetectorMethodInterceptor.java:57) ~[spring-data-commons-1.13.3.RELEASE.jar!/:?]
 at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.3.8.RELEASE.jar!/:4.3.8.RELEASE]
 at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213) ~[spring-aop-4.3.8.RELEASE.jar!/:4.3.8.RELEASE]
 at com.sun.proxy.$Proxy201.getSomeObject(Unknown Source) ~[?:?]
 at mypackage.getSomeObject(MyClass.java:111) ~[]

几周以来我一直在努力解决这个问题,但我不知道为什么会抛出这个异常 EntityNotFoundException。

异常是正确的,我不明白为什么它不像往常一样返回null。

更新:

public class SomeObject {

  @Id
  @GeneratedValue(strategy = GenerationType.AUTO)
  private Long id;

}

【问题讨论】:

  • 你能详细说明一下SomeObject 吗?是否从中引用了其他实体?
  • 不,是超级简单的Entity,更新了
  • 是否启用了二级缓存(例如 EhCache)?这可能导致 instance(2) 认为它知道 SomeObject,然后在查看数据库时感到惊讶。
  • @df778899 实际上我做到了,当我关闭它时,它工作正常。但这并没有真正的意义......你能添加你的答案,所以我可以给它+50分吗?
  • 那里有答案 - 随时使用您知道的任何其他内容进行编辑。如果缓存性能很重要,那么可能值得研究像 Terracotta 这样的共享缓存。前段时间的经验是,性能非常好,但这绝对是设置中另一个动人的部分。

标签: spring orm spring-data-jpa dao


【解决方案1】:

正如在 cmets 中建立的,启用了二级缓存(EhCache?)。

这样的效果是,在问题的示例中,instance(2) 在第二次检索进入时认为它记住了创建请求,但随后惊讶地发现在数据库中找不到匹配项。

【讨论】:

    【解决方案2】:

    不是现成的完整答案,而是一些关于如何调试这类事情的说明(而且评论太长了)。

    1. 确保您的 IDE 中提供并正确配置了 Spring Data JPA、Spring Data Commons 和 Spring Data ORM 的源代码。

    2. 获取您获得的异常的完整堆栈跟踪(不仅仅是您发布的部分)。

    3. 根据堆栈跟踪在代码中优雅地放置断点。

    4. 在调试器中运行不会引发异常的简单场景。记下您击中的断点和未击中的断点。

    5. 删除除最后一个以外的所有断点。在您点击的最后一个和您错过的第一个之间添加更多断点。

    6. 重复直到这两个断点仅相隔一个堆栈帧。

    7. 在两种情况下通过两个断点之间的剩余代码进行调试并观察差异。

    8. 回到这里,告诉我们您发现了什么作为问题的更新或答案,可能包括指向问题和/或拉取请求的链接。

    【讨论】:

    • 我确实试过了,但是很多调用都发生在代理方法中,有时我只是迷失在堆栈跟踪中。但我想我需要再试一次
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-01-24
    • 1970-01-01
    • 1970-01-01
    • 2018-07-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多