Hibernate Проблема кэширования запросов второго уровня с тем же предложением where - программирование
Подтвердить что ты не робот

Hibernate Проблема кэширования запросов второго уровня с тем же предложением where

В моем приложении используются JPA (1.2), Spring (3.1.2), Spring данные (1.1.0) и спящий режим (4.1.7). База данных: Oracle10g

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

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

Как и мой первый запрос (countRelease),

select count(r) from Release r where r.type in 
(select c.contentTypeId from ContentType c where c.parentContentTypeId is NULL)
order by r.validityStart

а второй запрос (findRelease) -

select r from Release r where r.type in 
(select c.contentTypeId from ContentType c where c.parentContentTypeId is NULL)   
order by r.validityStart

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

Если я удаляю кеш запросов, он работает нормально, и если я сделаю некоторые изменения во втором запросе, где предложение, то и он работает нормально, но мне не нужно это делать.

Как мы можем решить эту проблему?

Мой код Java

@Query(name="findRelease")
@QueryHints({@QueryHint(name = "org.hibernate.cacheRegion", value ="cvodrelease"),@QueryHint(name = "org.hibernate.cacheable", value ="true") })
public List<Release> findRelease();

@Query(name="countRelease")
@QueryHints({@QueryHint(name = "org.hibernate.cacheRegion", value ="cvodrelease"),@QueryHint(name = "org.hibernate.cacheable", value ="true") })
public Long  countOfRelease(Date today);

Конфигурация кэша

<property name="hibernate.cache.region.factory_class" value="org.hibernate.cache.ehcache.EhCacheRegionFactory"/>
<property name="hibernate.cache.use_query_cache" value="true"/>
<property name="hibernate.cache.use_second_level_cache" value="true"/>
<property name="hibernate.cache.provider_class" value="net.sf.ehcache.hibernate.EhCacheProvider" /> 
<property name="hibernate.cache.provider_configuration_file_resource_path" value="ehcache.xml" />

<bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager"  p:cacheManager-ref="ehcache"/>

<bean id="ehcache" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean" p:configLocation="ehcache.xml"  p:shared="true"/> 
4b9b3361

Ответ 1

Стандарт JPA 1.0 не включал кэширование (и JPA 1.2 не существует).

В стандарте JPA 2.0 введено кэширование - включая общий кеш ( "кеш первого уровня" для каждого экземпляра EntityManagerFactory) и кэш приложений (кеш второго уровня для экземпляров ALL EntityManagerFactor). Также каждый PersistenceContext для каждого экземпляра EntityManager действует как свой собственный кэш самого низкого уровня - "кеш нулевого уровня".

Это означает, что ваше поведение специфично для Hibernate 4.1.7 и не имеет никакого отношения ни к какому стандарту или к другому продукту.

Кэширование не используется, когда кеш данных не кэшируется данные для идентификатора в результате запроса.

Это прямая цитата из Apache OpenJPA docs, а не Hibernate или JPA spec. Вы можете игнорировать, но, похоже, это было бы верно для Hibernate.

Запросы, которые вызывают прогнозы пользовательских типов полей или полей BigDecimal или BigInteger, не кэшируются.

Это прямая цитата из Oracle Kodo JPA docs, а не Hibernate или JPA spec. Может быть разумно проигнорировать это.

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

Это прямая цитата из Hibernate 4.1 docs. Таким образом, вы можете следовать этому совету - до тех пор, пока вы берете его в контексте: он говорит, чтобы включить кеш второго уровня, если вы хотите кэшировать объекты, возвращаемые из запросов. Если вы не хотите кэшировать целые объекты сущностей, но просто хотите кэшировать результаты NamedQueries, содержащие примитивные типы данных (прогнозы), тогда вам понадобится кеш первого уровня.

Мое предложение:

  • Я думаю, что проблема может заключаться в том, что COUNT (r) возвращает BigInteger в java, который нельзя передать в Object для кэширования. Вы можете вызвать addScalar ( "count", Hibernate.LONG) в запросе, чтобы сообщить hibernate использовать другой тип - LONG. См. blog.pfa-labs.com/2009/12/caching-raw-sql-count-with-hibernate.html плюс Используется ли второй уровень кэша Hibernate для операций COUNT()?

  • Кэш запросов должен иметь возможность справиться с этим. Кэш второго уровня должен быть необходим только для объектов сущностей.

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

  • Имейте в виду, что некоторые драйверы JDBC также кэшируют данные - если это повлияет на результаты JPA, и JPA даже не узнает об этом.

От Майка Кейта "Pro JPA 2": Большинство [JDBC] драйверов кэшируют подключения и заявления. Некоторые кэши также отслеживают состояние таблицы или столбца, которое по существу прозрачно для провайдера JPA, но тем не менее может предложить некоторую экономию с точки зрения того, что вам не нужно обращаться к базе данных для получения данных по каждому звонку. Это обычно возможно в драйвере только в том случае, если известно, что либо данные доступны только для чтения, либо исключительно управляют доступом к базе данных исключительно для доступа к данным.

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

EDIT:

В первом запросе "order by r.validityStart" ничего не делает - вы можете удалить его, и все будет работать.

Ответ 2

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

Из документации:

  • Кэширование не используется, когда кэш данных не имеет кэшированных данных для идентификатора в результате запроса.

  • Запросы, которые вызывают прогнозы пользовательских типов полей или полей BigDecimal или BigInteger, не кэшируются.

  • Обратите внимание, что кеш запросов не кэширует состояние фактических объектов в результирующем наборе; он кэширует только значения идентификатора и результаты типа значения. Кэш запросов всегда должен использоваться в сочетании с кэшем второго уровня.

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

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

Ответ 3

Я считаю, что ваша проблема не связана с кэшем второго уровня - это что-то еще. Сам кеш не может изменить ожидаемый результат.

Чтобы быть еще точнее, вы можете попробовать следующий код, чтобы очистить кеш второго уровня до начала второго запроса:

session.setCacheMode(CacheMode.IGNORE); // session here is the SessionFactory

Если проблема все еще сохраняется, тогда ясно, что кеш второго уровня не является виновником.