1

我在长时间运行的对话中遇到了一级缓存问题(使用 JPA/Hibernate 和 Seam 2.2)。

我们有一个使用长时间运行对话的搜索页面(需要一些 ajax 交互、模态编辑等)。

问题是当另一个用户更改实体时,当用户再次单击搜索按钮时,不会显示更改。在这里看到类似的问题

因此,在长时间运行的会话中,实体从一级缓存加载,除非您手动调用每个实体上的刷新,否则不会加载其他事务中所做的更改。

我尝试使用 CacheMode.IGNORE 或 CacheMode.REFRESH (通过休眠查询),但这并不能解决问题,因为它只处理二级缓存。

我可以看到选择查询正在发送到数据库,但似乎 Hibernate 仍在使用一级缓存中的缓存对象。

在进行新搜索之前,我曾尝试从之前的搜索中驱逐实体,这很有效,但是后来我遇到了有线问题,因为会话中仍有对象指向现在被驱逐的实体。你必须添加

@org.hibernate.annotations.Cascade({org.hibernate.annotations.CascadeType.EVICT,org.hibernate.annotations.CascadeType.REFRESH})

关于您的实体关系,但很容易错过。

您也可以清除空洞会话,但这会导致 LazyInitializationErrors。

没有办法告诉 JPA/Hibernate 不要在查询中使用第一级缓存吗?

编辑:有一个很好的答案为什么它不起作用但刷新每个实体的建议并不理想。我以前用过这个,但问题是,例如,对于一个包含 200 个条目的列表,您会为数据库生成 200 个单独的负载,这使得它非常慢。在这 200 个条目中,只有一两个可能发生了变化!那么如何识别哪些实体发生了变化!我想如果您在每次更改时更新的实体上使用时间戳,您可以使用它。只需记录您之前搜索的时间戳,然后搜索列表中自那时以来已更改的实体。你怎么看,还有更优雅的解决方案吗?

4

1 回答 1

3

一种解决方法是在 EVENT 范围内定义一个单独的持久性上下文工厂,并将其用于查询。在components.xml

<persistence:managed-persistence-context scope="event" auto-create="true" 
    entity-manager-factory="#{yourfactory}" name="eventScopedEM" />

这样做是在每个页面事件上创建一个新的实体管理器,而不是在整个对话中保持相同的实体管理器。保留其他对话范围的持久性上下文工厂(默认情况下#{entityManager})以供所有其他用途。如果您使用EntityQuery组件进行查询,请记住设置实体管理器的名称(否则默认为entityManager),覆盖getPersistenceContextName()方法(对于基于 Java 的查询组件)或显式设置实体管理器(对于基于 xml 的查询组件),例如:

<framework:entity-query entityManager="#{eventScopedEM}">
    <framework:ejbql>from MyEntity</framework:ejbql>
    ...
</framework:entity-query>
于 2013-04-03T13:42:18.267 回答