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?
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.
I discovered the problem, but it introduces something else.
Basically, when modifying a Book
object's List<PageContent>
field, Hibernate does three things:
- Expires the TimeStamp cache entry for both
Book
andPageContent
- Does many queries to reset the
pageNum
field on eachPageContent
object - Removes the
Book
object from the Second Level Cache.
This ensures that subsequent queries will search for new objects, etc. However:
- 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.
来源:https://stackoverflow.com/questions/6064083/why-is-my-hibernate-query-returning-stale-data