Почему мой запрос Hibernate возвращает устаревшие данные?
-
16-11-2019 - |
Вопрос
Быстрая версия
По сути, я обновляю таблицу 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()
хотя.