4

快速版

基本上,我正在更新一个休眠表,随后的查询正在加载一个陈旧的值。

详细版本

休眠 (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);
}

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

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

所以,我不情愿地从标准中删除缓存。它查询数据存储,但仍然返回错误的值。但是,在这两种情况下,如果我等待大约 2 分钟,一切都会按预期工作。我认为仍然涉及缓存。两者都PageContent使用Book此缓存策略:

@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:通过调试,我可以确认数据存储具有正确的信息,并且当查询运行时,它会退回到二级缓存——它有脏信息。我认为每次数据更改时都不能从缓存中逐出?

4

2 回答 2

0

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

于 2011-05-19T20:44:47.957 回答
0

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

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

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

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

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

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

我推测这是因为 Hibernate 觉得pageNum变化不是数据的变化,而是幕后管理的变化。但是,这是我想要读取和显示的数据。

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

于 2011-05-25T16:37:36.290 回答