一尘不染

为什么我的Hibernate查询返回陈旧数据?

hibernate

快速版本

基本上,我正在更新hibernate表,后续查询正在加载陈旧的值。

详细版本

hibernate(3.3.1.GA)和EhCache(2.4.2)。

包含页面的持久Book对象,List<PageContent>我将页面添加到本书的中间。我正在使用Databinder /
Wicket,尽管我认为这并不相关。

 public void createPageContent(Book book, int index) {
     Databinder.getHibernateSession().lock(book, LockMode.UPGRADE);
     PageContent page = new PageContent(book);
     book.addPage(page, index);
     CwmService.get().flushChanges(); // commits the transaction
 }

适用的字段/方法Book是:

@OneToMany
@JoinColumn(name="book_id")
@IndexColumn(name="pageNum")
@Cascade({CascadeType.ALL, CascadeType.DELETE_ORPHAN})
private List<PageContent> pages = new ArrayList<PageContent>();

public synchronized void addPage(PageContent page, int index) {
    pages.add(index, page);
}

最终结果是,有一个新页面添加到列表中,并且数据库也进行了相应更新,我已经在我的数据存储中确认了这一点。但是,对页面的下一个查询(例如“页面#4”)将加载“旧”页面#4,而不是新页面#4:

criteria.add(Restrictions.eq("book", book));
criteria.add(Restrictions.eq("pageNum", pageNum));
criteria.setCacheable(true);

因此,我勉强从条件中删除了缓存。它查询数据存储,但 仍返回错误的value
。但是,在两种情况下,如果我等待约2分钟,一切都会按预期进行。我认为仍然涉及缓存。双方PageContentBook使用该缓存策略:

@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)

我承认我是缓存的新手,只是第一次设置此文件。这是我的ehcache.xml:

<defaultCache maxElementsInMemory="10000" eternal="false" timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="true" diskSpoolBufferSizeMB="30" maxElementsOnDisk="10000000" diskPersistent="false" diskExpiryThreadIntervalSeconds="120" memoryStoreEvictionPolicy="LRU" statistics="false"/>

<!-- Hibernate's Cache for keeping 'lastUpdated' data on each table.  Should never expire. -->
<cache name="org.hibernate.cache.UpdateTimestampsCache" eternal="true" />

<!-- Hibernate's Query Cache - should probably be limited -->
<cache name="org.hibernate.cache.StandardQueryCache" maxElementsInMemory="1000" />

更新 :删除@Cache我的数据存储对象上的注释可消除问题。当然,我想缓存这些对象,因为页面修改的频率远低于访问。

那么,有什么想法吗?还有其他一些相关问题,包括删除页面。一切都会按预期更新数据库,但是实际行为却很奇怪。

提前致谢!

更新#2
:通过调试,我可以确认数据存储区具有正确的信息,并且当查询运行时,它会退回到具有脏信息的二级缓存中。我想是不是每次数据更改时都要从缓存中逐出?


阅读 325

收藏
2020-06-20

共1个答案

一尘不染

我发现了问题,但引入了其他问题。

基本上,在修改Book对象的List<PageContent>字段时,Hibernate会执行三件事:

  1. 过期两个时间戳缓存条目BookPageContent
  2. 进行许多查询以重置pageNum每个PageContent对象上的字段
  3. Book从二级缓存中删除对象。

这样可以确保后续查询将搜索新对象等。但是:

  1. Hibernate无法PageContent从二级缓存中删除每个重新编号的对象

结果,对页面列表的任何查询都将正常运行,但随后将退回到实际数据的陈旧二级缓存值。

我认为这是因为Hibernate认为pageNum更改不是数据更改,而是后台管理的更改。但是,这就是我要读取和显示的数据。

解决方案是在插入/删除发生后手动刷新每个页面。

2020-06-20