【问题标题】:Why JPA entity refresh is required despite disabling shared cache?尽管禁用了共享缓存,为什么需要 JPA 实体刷新?
【发布时间】:2017-11-09 08:42:45
【问题描述】:

我正在为 JPA 使用 Eclipselink,并且有一个 Web 应用程序可以在配置的时间间隔内读取表格。数据库通过工具在外部进行修改以发布一些数据。现在我的 webapp 在一个实体上创建一个 getAll() 并将结果作为一个列表。但是,每次我得到与第一个查询相同的结果列表时。令人惊讶的是,新条目和已删除条目会按预期反映在结果列表中。如果我更新一个条目,它仍然会得到原始/旧的结果。

从我的搜索来看,stackoverflow中的大部分答案都指向了JPA级别的缓存,可以总结为这些

  • 通过设置禁用共享缓存<shared-cache-mode>NONE</shared-cache-mode>
  • (可选)使用@Cacheable(false) 选择性地禁用实体上的缓存
  • 通过em.getEntityManagerFactory().getCache().evictAll() 清除缓存
  • 还有一些其他/旧/pre-JPA 2.0 和供应商特定的解决方案在 persistence.xml 中设置了一些属性

但是,这些解决方案都不适用于我的情况。我遇到的最接近的答案是this, which says that

会话缓存中已存在的任何 ID 都与已知实体匹配,而任何未根据数据库状态填充的 ID。以前已知的实体根本不会从数据库中刷新。

虽然这是在 Hibernate 的上下文中,但我在 Eclipselink 中遇到了同样的问题。因此,在列表中引入刷新后,我得到了预期的结果。

T objects = getAll(); 
for (T objects : object) 
    em.refresh(object) 

List<T> getAll() {
  EntityManager em = entityManagerFactory.createEntityManager();
  return  em.createQuery(em.getCriteriaBuilder().createQuery(type)).getResultList();
}

有人可以在 Eclipselink 的上下文中解释这个Session cache,并解释为什么shared-cache 配置对此没有影响吗?事务是否透明地解决了这个问题(读取事务)?有没有比每次都刷新对象更优雅的解决方案/JPA 配置?

【问题讨论】:

    标签: java hibernate jpa eclipselink jpa-2.0


    【解决方案1】:

    从 JPA 2.0 开始,您可以使用查询提示绕过或刷新缓存。

    Query query = entitymanager.createQuery("select student FROM Student student");
    query.setHint("javax.persistence.cache.retrieveMode", CacheRetrieveMode.BYPASS);
    

    Here 是一个很好的解释

    【讨论】:

    • 谢谢。我知道查询提示。这带来了 2 个问题 1)我们可能必须将其应用于每种类型的查询 2)如果我们只是想选择性地禁用某些实体的缓存怎么办。例如。就像在@Cacheable 注释中一样。
    【解决方案2】:

    您提到的所有内容均指共享缓存,但 JPA 中有 2 级缓存。共享缓存(第 2 级)和 EntityManager 托管实体缓存(第 1 级)。 EntityManager 旨在表示事务工作区。您应该在适当的情况下获取新的 Entitymanager 实例,而不是使用单个实例,或者调用 em.clear() 来释放资源。

    【讨论】:

    • 我每次都使用一个新的实体管理器实例。请查看问题中的 sn-p。
    • 您调用 getAll 获得自己的 em 然后在此方法调用之外调用 refresh 似乎很奇怪 - 它是同一个 em 还是不同的em?您在哪里进行了这些更改,您是如何配置的(即什么服务器,您如何获得 entitymanagerFactory,它是如何配置的等等)。
    • 我使用 guice-persist 并为 EntityManager 注入一个 Provider。 guice 持久化服务在 webapp 启动时启动。 findAll() 将通过执行 emProvider.get() 获得 entityManager。
    • 它可能会给你一个超越 EM 的代理。你必须检查它是否每次都给你一个新的,或者在事务之外重用 EntityManagers。
    猜你喜欢
    • 1970-01-01
    • 2011-07-27
    • 2014-12-02
    • 1970-01-01
    • 1970-01-01
    • 2011-10-20
    • 2019-07-13
    • 2021-08-10
    • 2018-06-06
    相关资源
    最近更新 更多