Атрибут области аннотации Hibernate Cache не работает

У меня есть приложение spring/springwebflow (приложение A), которое использует кеш второго уровня Hibernate, а также кеш запросов. Приложение только читает из базы данных, операторы добавления/обновления/удаления невозможны.

Запросы кэшируются в одном регионе под названием «daoCache». Я установил это с помощью аннотации к namedQuerys

У меня есть объект с аннотацией из Hibernate @Cache с атрибутом region, установленным для «daoCache».

@Entity
@Table(name = "GROUP_OPERATOR")
@SequenceGenerator(name = "SEQ_GROUP_OPERATOR", sequenceName = "SEQ_GROUP_OPERATOR")
@NamedQueries( {
@NamedQuery(name = "findGroupOperatorByMaster", query = "FROM GroupOperator groupOperator WHERE groupOperator.segment IS NULL and groupOperator.pointsMaster.pointMaster like ?",
    hints={@QueryHint(name="org.hibernate.cacheRegion", value="daoCache"),
       @QueryHint(name="org.hibernate.cacheable", value="true")})
})
@Cache(usage = CacheConcurrencyStrategy.READ_ONLY, region = "daoCache")
public class GroupOperator implements Serializable{
 ....

Помимо этого, у меня есть другое приложение (приложение B), которое подключается к той же базе данных и выполняет добавление/обновление/удаление в той же базе данных.

По этой причине мы установили ограничение в 20 минут для обновления области кеша в приложении А. Таким образом, изменения, сделанные в приложении Б, отражаются в приложении А через 20 минут.

Итак, давайте предположим, что приложение A выполняет запрос «выберите * из group_operator, где point_master = 1000». Этот запрос будет сохранен в кеше с идентификатором результата, скажем, groupOperator#1. Таким образом, в любое другое время (между 20 минутами), когда этот запрос выполняется, он вернет из кеша идентификатор #1. И сущность также будет сохранена в том же регионе кеша.

Проблема в том, что приложение Б удаляет запись и создает новую (с другим идентификатором), но *с теми же значениями, которые используются в качестве параметров в запросе*

Таким образом, между 20 минутами приложение A вернет объект groupOperator # 1, и в журнале приложения не будут отображаться операторы выбора. Это ожидаемое поведение.

Через 20 минут кеш будет сброшен.

Теперь приложение А пытается выполнить тот же запрос («выбрать * из оператора группы, где точка_мастер = 1000»). Он будет делать запрос к базе данных, потому что кеш был очищен через 20 минут. Таким образом, результатом должен быть новый идентификатор, созданный в приложении B. Но вместо этого я получаю исключение ObjectNotFoundException. Строка с данным идентификатором не существует: groupOperator#1

Даже операторы select отображаются в журнале, но по-прежнему возвращают старый идентификатор объекта. И, конечно же, ID 1 больше нет в базе данных.

Это очень странно, потому что я думал, что очистка кеша снова выполнит запрос

Если и объект, и запрос кэшируются в одном и том же регионе, почему все еще ищет старый объект?

После нескольких дней изучения этого с помощью дампов кучи я обнаружил, что объект не кэшируется в «daoCache», указанном в аннотации @Cache. Но кэшируется в новом регионе с именем «xxx.xxx.xxx.xx.groupOperator». Кажется, атрибут для этой аннотации не работает.

у меня есть пара вопросов

1-Почему атрибут региона в аннотации кэша не работает? Даже если я установил «daoCache», он сохраняется в другом? это баг спящего режима?

2-Как именно работает кэш запросов? Я имею в виду, что если запрос выполняется снова, он должен вернуть новый идентификатор, а затем с этим новым идентификатором hibernate должен выполнить метод загрузки, и проблема будет решена. Но все равно пытаюсь загрузиться со старого идентификатора.

Это моя фактическая конфигурация ehcache.xml

<cache name="daoCache" maxElementsInMemory="30000" eternal="false"
        timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="true">
    </cache>

    <cache name="rwCache" maxElementsInMemory="30000" eternal="false"
        timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="true">
    </cache>

    <cache name="xxx.xxxx.xxx.xxxx.groupOperator" maxElementsInMemory="30000"     eternal="false"
        timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="true">
    </cache>

Это мой провайдер кеша

<prop key="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</prop>

Использование следующей версии спящего режима

<dependency>
     <groupId>org.hibernate</groupId>
     <artifactId>hibernate-ehcache</artifactId>
     <version>3.3.2.GA</version>
</dependency>


<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-core</artifactId>
    <version>3.3.2.GA</version>
</dependency>

<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-commons-annotations
    </artifactId>
    <version>3.2.0.Final</version>
</dependency>

Спасибо за ваше время

ОБНОВЛЕНИЕ:

Я не указал никакого hibernate.cache.region.factory_class. Так что я думаю, что используется по умолчанию. Не знаю, какой из них по умолчанию

Запросы имеют аннотацию queryhint в классе. Это работает хорошо, кэширует запрос, даже если я удаляю все записи из таблицы, все равно возвращается старая запись без выполнения какого-либо выбора в БД. Так что кеш запросов отлично работает.

Проблемы в следующем:...

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

После очистки кеша (автоматически через 20 минут) запрос снова выполняется в базе данных (что нормально, и я вижу в журнале операторы select). Новая запись в таблице найдена, и она должна вернуть идентификатор, чтобы затем загрузить всю сущность в память. Странно то, что когда он пытается получить весь объект в памяти, он ищет этот объект, используя старый идентификатор, а не новый, который я добавил ранее. Это похоже на то, что запрос, даже если он снова выполняется в базе данных, все равно возвращает старый идентификатор. Но повторюсь, кеш запросов работает как обычно в других сценариях и это точно.

Итак, я не понимаю, как кеш запросов и кеш сущностей работают вместе. Похоже на проблему между кешем запросов и кешем сущностей. Я говорю это, потому что решил эту проблему, добавив в ehcache.xml конфигурацию региона для этой сущности.

<cache
    name="com.citi.latam.business.services.dao.model.db.campaign.groupOperator"
    maxElementsInMemory="30000" eternal="false" timeToIdleSeconds="120"
    timeToLiveSeconds="120" overflowToDisk="true">
</cache>

person user3320292    schedule 17.02.2014    source источник


Ответы (2)


ФИКСИРОВАННЫЙ!

Я наконец решил. Во-первых, я включил всю отладку из спящего режима и ehcache.

Поискав в журналах, я обнаружил неправильную конфигурацию моей модели базы данных.

Object GroupOperator имеет свойство Calendar. Проблема заключалась в том, что у объекта Calendar также есть List of GroupOperator с аннотацией Cache. Таким образом, каждый раз, когда Hibernate получает новый GroupOperator, также требуется обновить дочерние объекты, такие как Calendar, но у календаря есть свой собственный список GroupOperator, и кажется, что дочерние объекты не находятся в той же области кеша или работают по-другому.

Проблема решена! Спасибо за ваше время, @jhadesdev, было полезно продолжить расследование.

С уважением,

person user3320292    schedule 21.02.2014

Какой hibernate.cache.region.factory_classвы используете, EhCacheRegionFactory или SingletonEhCacheRegionFactory? Это может относиться к пункту 1.

Для пункта 2 это работает, как вы упомянули, но запросы должны быть помечены как кэшируемые отдельно. В API критериев это делается с помощью setCacheable(true), а в JPQL — с подсказкой запроса.

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

Запросы по умолчанию кэшируются в области кеша по умолчанию, но можно выбрать область, в которой запрос кэшируется с помощью API criteria.setCacheRegion().

Поместив рассматриваемый запрос в ту же область кэша, что и объект, это гарантирует, что и кэшированные результаты запроса, и кэшированные объекты будут удалены одновременно.

person Angular University    schedule 17.02.2014
comment
Я ответил вам обновлением в моем вопросе. Взгляни, пожалуйста. Спасибо - person user3320292; 19.02.2014
comment
Я отредактировал ответ для одновременного удаления кешей, но для выбора, попадающего в базу данных и по-прежнему возвращающего древние значения, что возможно только в том случае, если транзакция удаления/вставки не была зафиксирована или если уровень изоляции REPEATABLE_READ/SERIALIZABLE сохраняется для огромное количество времени (20 минут) службы, выполняющей периодический запрос - person Angular University; 20.02.2014
comment
Спасибо! На самом деле я выполняю транзакцию удаления вне основного приложения (из приложения B). Таким образом, основное приложение не знает об изменении, сделанном из приложения B. Это сценарий: приложение A читает, приложение B удаляет запись, приложение B создает новую запись с теми же значениями, что и удаленная, но с другим идентификатором, приложение A снова читает , создается исключение NotEntityFoundException. Я предполагаю, что сеанс гибернации в приложении А не знает об изменении в таблице, поэтому спящий режим предполагает, что изменений не было вообще, и все еще ищет древний идентификатор. Может быть, это как-то связано с UpdateTimestampsCache, посмотрю - person user3320292; 21.02.2014