我在长时间运行的对话中遇到了第一级缓存的问题(使用JPA / Hibernate和Seam 2.2)。
我们有一个搜索页面,它使用长时间运行的对话(某些ajax交互需要,模态编辑等)。
问题是,当其他用户更改实体时,当用户再次单击搜索按钮时,不会显示更改。 见similar problem here
因此,在长时间运行的对话中,实体从第一级缓存加载,除非您在每个实体上手动调用刷新,否则不会加载在其他事务中进行的更改。
我尝试使用CacheMode.IGNORE或CacheMode.REFRESH(通过hibernate查询),但这并不能解决问题,因为它只处理二级缓存。
我可以看到选择查询被发送到数据库,但似乎Hibernate仍在使用第一级缓存中的缓存对象。
我尝试在进行新搜索之前从我以前的搜索中逐出实体并且这样做有效,但是我之后遇到了有线问题,因为会话中仍然存在指向现在被驱逐的实体的对象。 你必须添加
@org.hibernate.annotations.Cascade({org.hibernate.annotations.CascadeType.EVICT,org.hibernate.annotations.CascadeType.REFRESH})
关于您的实体关系,但很容易错过一个。
您也可以清除空洞会话,但这可能会导致LazyInitializationErrors。
有没有办法告诉JPA / Hibernate不要在查询中使用第一级缓存?
编辑:Good answer为什么它不起作用,但刷新每个实体的建议并不理想。我以前用过这个,但问题是,例如对于200个条目的列表,您将为数据库生成200个单独的加载,这使得它非常慢。 在这200个条目中,只有一两个可能已经改变! 那么如何识别哪些实体已经改变了!我想如果你对每个更改上更新的实体使用时间戳,你可以使用它。只需记录上一次搜索的时间戳,然后搜索列表中自那时以来已更改的实体。你觉得怎么样,那里有更优雅的解决方案?
答案 0 :(得分: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>