У меня есть приложение 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>