为什么我的 Hibernate 查询返回过时的数据?

发布于 2024-11-08 12:24:32 字数 2429 浏览 4 评论 0原文

快速版本

基本上,我正在更新 Hibernate 表,后续查询正在加载过时的值。

详细版本

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

保留带有页面 ListBook 对象,并且我将在本书的中间添加一个页面。我正在使用 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 分钟,一切都会按预期进行。我认为仍然涉及缓存。 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:通过调试,我可以确认数据存储区具有正确的信息,并且当查询运行时,它会退回到二级缓存 - 其中包含脏信息。我想我不应该在每次数据更改时从缓存中逐出?

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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

入怼 2024-11-15 12:24:32

在 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 about flushChanges() though.

末骤雨初歇 2024-11-15 12:24:32

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

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

  1. 使 Book的 TimeStamp 缓存条目过期>PageContent
  2. 执行多次查询以重置每个 PageContent 对象上的 pageNum 字段
  3. 从二级缓存中删除 Book 对象。

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

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

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

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

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

I discovered the problem, but it introduces something else.

Basically, when modifying a Book object's List<PageContent> field, Hibernate does three things:

  1. Expires the TimeStamp cache entry for both Book and PageContent
  2. Does many queries to reset the pageNum field on each PageContent object
  3. Removes the Book object from the Second Level Cache.

This ensures that subsequent queries will search for new objects, etc. However:

  1. Hibernate fails to remove each renumbered PageContent object from the Second Level Cache

As 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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文