Почему мой запрос 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 минут, все работает как надо. ожидаемый. Я предполагаю, что кэширование все еще задействовано. И 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" />
UPDATE : Удаление аннотаций @Cache
на моих объектах хранилища данных устраняет проблему. Конечно, я хотел бы кэшировать эти объекты, потому что изменение страницы происходит гораздо реже, чем доступ.
Итак, мысли? Есть несколько других проблемы, связанные также, в том числе с удалением страниц. Все обновляет базу данных, как и ожидалось, но фактическое поведение шатается.
Заранее спасибо!
UPDATE #2 : с помощью отладки я могу подтвердить, что хранилище данных содержит правильную информацию, и когда запрос выполняется, он возвращается в кэш второго уровня, который содержит грязную информацию. Я предполагаю, что это не зависит от меня, чтобы выселить из кэша каждый раз, когда данные меняются?
2 ответа:
После
CwmService.get().flushChanges(); // commits the transaction
сделайте явный коммит.flush()
только сбрасывает изменения в БД, но не фиксирует их. Хотя я не уверен насчетflushChanges()
.
Я обнаружил проблему, но она вводит кое-что еще.
В основном, при изменении поля
Book
объектаList<PageContent>
, Hibernate делает три вещи:
- истекает срок действия записи кэша временных меток для
Book
иPageContent
- выполняет множество запросов для сброса поля
pageNum
для каждого объектаPageContent
- удаляет объект
Book
из кэша второго уровня.Это гарантирует, что последующие запросы будут искать новые объекты и т. д. Однако:
- Hibernate не удается удалить каждый перенумерованный объект
PageContent
из кэша второго уровняВ результате любой запрос для списка страниц будет выполняться правильно, но затем будет возвращаться к устаревшим значениям кэша второго уровня для фактических данных.
Я предполагаю, что это происходит потому, что Hibernate чувствует, что изменениеpageNum
-это не изменение данных, а изменение закулисного управления. Тем не менее, это те данные, которые я хотел бы прочитать и отобразить.В решение состоит в том, чтобы вручную обновить каждую страницу после вставки / удаления.