为什么我的Hibernate Query会返回陈旧数据?

时间:2011-05-19 19:49:44

标签: java hibernate wicket ehcache databinder

快速版

基本上,我正在更新一个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);  

所以,我勉强从标准中删除了缓存。它查询数据存储区,但仍然返回错误的值。但是,在这两种情况下,如果我等待大约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注释会消除此问题。当然,我想缓存这些对象,因为页面修改比访问频繁得多。

那么,想法?还有其他一些相关问题,包括删除页面。所有内容都按预期更新数据库,但实际行为很不稳定。

提前致谢!

UPDATE#2 :通过调试,我可以确认数据存储区有正确的信息,当查询运行时,它会回退到二级缓存 - 它有脏信息。我认为每次数据发生变化都不能从缓存中逐出来?

2 个答案:

答案 0 :(得分:0)

CwmService.get().flushChanges(); // commits the transaction执行显式提交后。 flush()仅刷新对db的更改,但不提交它。我不确定flushChanges()

答案 1 :(得分:0)

我发现了这个问题,但它引入了其他的东西。

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

  1. 使BookPageContent
  2. 的TimeStamp缓存条目失效
  3. 是否有许多查询要重置每个pageNum对象上的PageContent字段
  4. 从二级缓存中删除Book对象。
  5. 这可确保后续查询将搜索新对象等。但是:

    1. Hibernate无法从二级缓存中删除每个重新编号的PageContent对象
    2. 因此,对页面列表的任何查询都将正常运行,但随后将回落到实际数据的陈旧二级缓存值。

      我认为这是因为Hibernate认为pageNum更改不是数据的变化,而是幕后管理的变化。但是,这是我想要阅读和显示的数据。

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