Почему мой 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 делает три вещи:
- Истекает запись кэша TimeStamp для обоих
Book
а такжеPageContent
- Много ли запросов для сброса
pageNum
поле на каждомPageContent
объект - Удаляет
Book
объект из кэша второго уровня.
Это гарантирует, что последующие запросы будут искать новые объекты и т. Д. Однако:
- Hibernate не может удалить каждый перенумерованный
PageContent
объект из кэша второго уровня
В результате любой запрос для списка страниц будет выполняться правильно, но затем будет возвращаться к устаревшим значениям кэша второго уровня для фактических данных.
Я предполагаю, что это потому, что Hibernate чувствует pageNum
изменение - это не изменение данных, а изменение закулисного управления. Тем не менее, это данные, которые я хотел бы прочитать и отобразить.
Решение состоит в том, чтобы вручную обновлять каждую страницу после вставки / удаления.
После CwmService.get().flushChanges(); // commits the transaction
сделать явный коммит.flush()
только сбрасывает изменения в базу данных, но не фиксирует их. Я не уверен насчет flushChanges()
хоть.