为什么我的 Hibernate 查询返回过时的数据?
快速版本
基本上,我正在更新 Hibernate 表,后续查询正在加载过时的值。
详细版本
Hibernate (3.3.1.GA) 和 EhCache (2.4.2)。
保留带有页面 List
的 Book
对象,并且我将在本书的中间添加一个页面。我正在使用 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:通过调试,我可以确认数据存储区具有正确的信息,并且当查询运行时,它会退回到二级缓存 - 其中包含脏信息。我想我不应该在每次数据更改时从缓存中逐出?
Quick Version
Basically, I'm updating a Hibernate Table and subsequent queries are loading a stale value.
Detailed Version
Hibernate (3.3.1.GA) and EhCache (2.4.2).
Persisted Book
object with a List<PageContent>
of pages and I'm adding a page to the middle of this book. I'm using Databinder/Wicket, though I do not think that is related.
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
}
The applicable fields/method in Book
are:
@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);
}
The end result is that there is a new page added to a list and the database is updated accordingly and I've confirmed this in my datastore. However, the next query for a page, say "Page #4," loads the "old" Page #4 instead of the new Page #4:
criteria.add(Restrictions.eq("book", book));
criteria.add(Restrictions.eq("pageNum", pageNum));
criteria.setCacheable(true);
So, I grudgingly remove caching from the criteria. It queries the datastore, but still returns the wrong value. However, in both cases, if I wait about 2 minutes, everything is working as expected. I presume caching is still involved. Both PageContent
and Book
use this caching strategy:
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
I confess I'm new to caching and just set up this file for the first time. Here's my 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" />
UPDATE: Removing the @Cache
annotations on my datastore objects removes the problem. Of course, I would like to cache these objects because page modification is much less frequent than access.
So, thoughts? There are several other issues related as well, including with deleting pages. Everything updates the database as expected, but actual behavior is wonky.
Thanks in advance!
UPDATE #2: Via debugging, I can confirm that the Datastore has the correct information and when the query runs, it falls back on the Second-Level Cache - which has dirty information. I presume it's not up to me to evict from the cache every time the data changes?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
在 CwmService.get().flushChanges() 之后; // 提交事务 执行显式提交。
flush()
仅将更改刷新到数据库,但不提交。但我不确定flushChanges()
。After
CwmService.get().flushChanges(); // commits the transaction
do an explicit commit.flush()
only flushes the changes to db but does not commit it. I am not sure aboutflushChanges()
though.我发现了这个问题,但它引入了其他东西。
基本上,当修改
Book
对象的List
字段时,Hibernate 会执行三件事:Book
和的 TimeStamp 缓存条目过期>PageContent
PageContent
对象上的pageNum
字段Book
对象。这确保了后续查询将搜索新对象等。但是:
PageContent
对象因此,对页面列表的任何查询都将正确运行,但随后将回退到实际数据的陈旧二级缓存值。
我认为这是因为 Hibernate 认为 pageNum 的更改不是数据的更改,而是幕后管理的更改。然而,这就是我想要读取和显示的数据。
解决方案是在插入/删除发生后手动刷新每个页面。
I discovered the problem, but it introduces something else.
Basically, when modifying a
Book
object'sList<PageContent>
field, Hibernate does three things:Book
andPageContent
pageNum
field on eachPageContent
objectBook
object from the Second Level Cache.This ensures that subsequent queries will search for new objects, etc. However:
PageContent
object from the Second Level CacheAs a result, any query for the list of pages will run properly, but then will fall back on stale Second Level Cache values for the actual data.
I presume this is because Hibernate feels a
pageNum
change is not a change in data but a change in behind-the-scenes management. However, that is the data that I would like to read and display.The solution is to manually refresh every page after the insertion/deletion has occurred.