java – Hibernate第二级查询缓存问题与where子句相同

我的应用程序使用JPA(1.2),Spring(3.1.2),Spring Data(1.1.0)和Hibernate(4.1.7).
DataBase: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

如果首先运行第一个查询,那么计数将会到来,如果我运行第二个查询,那么也会计数将会给我发布实体的列表.

如果我删除查询缓存,它的工作正常,如果我在第二个查询where子句中进行一些更改,那么它的工作正常,但我不需要这样做.

我们如何解决这个问题?

我的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"/> 
最佳答案
JPA 1.0标准没有缓存(JPA 1.2不存在).

JPA 2.0标准引入了缓存 – 包括共享缓存(每个EntityManagerFactory实例的“一级缓存”)和应用程序缓存(所有EntityManagerFactor实例的第二级缓存).每个EntityManager实例的每个PersistenceContext也作为自己的最低级缓存 – “零级缓存”.

这意味着您的行为是Hibernate 4.1.7特有的,与任何标准或任何其他产品无关.

Caching is not used when the the data cache does not have any cached
data for an id in a query result.

这是Apache OpenJPA文档的直接引用,而不是Hibernate或JPA规范.你可以忽略,但似乎这对Hibernate来说是正确的.

Queries that result in projections of custom field types or BigDecimal or BigInteger fields are not cached.

这是Oracle Kodo JPA文档的直接引用,而不是Hibernate或JPA规范.忽略这个可能是明智的.

The query cache does not cache the state of the actual entities in the cache. It caches identifier values and results of value type. Therefore, always use the query cache in conjunction with the second-level cache for those entities which should be cached as part of a query result cache. .

这是Hibernate 4.1文档的直接引用.所以你可以遵循这个建议 – 只要你把它放在上下文中:如果你想要缓存从查询返回的实体,那就是包含二级缓存.如果您不想缓存整个实体对象,而只是想缓存包含原始数据类型(投影)的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 plus Is/Can Hibernate’s Second-Level Cache be Used for COUNT() operations?
>查询缓存应该能够处理这个.只有实体对象才需要第二级缓存.
>非常小心,您了解您尝试缓存的对象的读/写行为 – 并确保读取次数远远大于写入次数.否则,缓存可能没有任何好处,甚至可以减缓事情,并导致数据不一致.
请注意,某些JDBC驱动程序还会缓存数据 – 如果您的数据将影响JPA结果,并且JPA甚至不会知道它.

来自Mike Keith的“Pro JPA 2”:
大多数[JDBC]驱动程序缓存连接和语句.一些缓存还跟踪JPA提供商基本上透明的表或列状态,但是这些缓存可以在不必去数据库获取每个呼叫的数据方面节省一些成本.只有知道数据是只读的或者驱动程序才能专门控制数据库访问,驱动程序通常是可行的.

JDBC缓存(如果可用)应通过驱动程序特定的配置设置来控制.

编辑:

在第一个查询中,“由r.validityStart命令”不执行任何操作 – 您可以将其删除,并且所有操作都将正常运行.

转载注明原文:java – Hibernate第二级查询缓存问题与where子句相同 - 代码日志