Почему мой запрос 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 4

2 ответа:

После CwmService.get().flushChanges(); // commits the transaction сделайте явный коммит. flush() только сбрасывает изменения в БД, но не фиксирует их. Хотя я не уверен насчет flushChanges().

Я обнаружил проблему, но она вводит кое-что еще.

В основном, при изменении поля Book объекта List<PageContent>, Hibernate делает три вещи:

  1. истекает срок действия записи кэша временных меток для Book и PageContent
  2. выполняет множество запросов для сброса поля pageNum для каждого объекта PageContent
  3. удаляет объект Book из кэша второго уровня.

Это гарантирует, что последующие запросы будут искать новые объекты и т. д. Однако:

  1. Hibernate не удается удалить каждый перенумерованный объект PageContent из кэша второго уровня

В результате любой запрос для списка страниц будет выполняться правильно, но затем будет возвращаться к устаревшим значениям кэша второго уровня для фактических данных.

Я предполагаю, что это происходит потому, что Hibernate чувствует, что изменение pageNum-это не изменение данных, а изменение закулисного управления. Тем не менее, это те данные, которые я хотел бы прочитать и отобразить.

В решение состоит в том, чтобы вручную обновить каждую страницу после вставки / удаления.