Почему мой Hibernate Query возвращает устаревшие данные?

Быстрая версия

По сути, я обновляю Hibernate Table, а последующие запросы загружают устаревшие значения.

Подробная версия

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" />

ОБНОВЛЕНИЕ: Удаление @Cache аннотации к моим объектам хранилища данных устраняют проблему. Конечно, я хотел бы кэшировать эти объекты, потому что модификация страницы гораздо реже, чем доступ.

Итак, мысли? Есть еще несколько вопросов, связанных с удалением страниц. Все обновляет базу данных, как и ожидалось, но фактическое поведение является шатким.

Заранее спасибо!

ОБНОВЛЕНИЕ № 2: С помощью отладки я могу подтвердить, что хранилище данных имеет правильную информацию, и когда запрос выполняется, он возвращается к кэшу второго уровня, который содержит грязную информацию. Я полагаю, это не мое дело, чтобы выгнать из кэша каждый раз, когда данные изменяются?

2 ответа

Решение

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

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

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

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

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

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

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

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

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

Другие вопросы по тегам