我的应用程序使用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如果第一个查询是第一次运行,那么count就会来,如果我运行第二个查询,那么count也会来,它应该会给我释放实体的列表。
如果我删除了查询缓存,它工作得很好,如果我在第二个查询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"/> 发布于 2013-03-11 15:07:21
JPA 1.0标准没有包含缓存( JPA 1.2也不存在)。
JPA2.0标准引入了缓存-包括共享缓存(每个EntityManagerFactory实例的“一级缓存”)和应用程序缓存(所有EntityManagerFactor实例的二级缓存)。此外,每个EntityManager实例的每个PersistenceContext都充当其自己的低级缓存--“零级缓存”。
这意味着你的行为都是Hibernate 4.1.7特有的,与任何标准或任何其他产品无关。
当数据缓存没有查询结果中id的任何缓存数据时,不使用
缓存。
这是直接引用Apache OpenJPA文档,而不是Hibernate或JPA规范。你可以忽略这一点,但这似乎对Hibernate是正确的。
不会缓存导致自定义字段类型投影或BigDecimal或BigInteger字段投影的
查询。
这是直接引用自Oracle Kodo JPA文档,而不是Hibernate或JPA规范。忽略这一点可能是明智的。
查询缓存不缓存缓存中实际实体的状态。它缓存标识符值和值类型的结果。因此,对于那些应该作为查询结果缓存的一部分进行缓存的实体,请始终将查询缓存与二级缓存结合使用。。
这是从Hibernate 4.1文档中直接引用的。所以你可以遵循这个建议-只要你把它放在上下文中:如果你想缓存从查询返回的实体,就应该包括二级缓存。如果不想缓存整个实体对象,只想缓存包含原始数据类型(投影)的NamedQueries结果,那么一级缓存就足够了。
我的建议是:
摘自Mike Keith的"Pro JPA 2":大多数JDBC驱动程序缓存连接和语句。一些缓存还会跟踪表或列的状态,这些状态对JPA提供程序基本上是透明的,但就不必在每次调用时都去数据库获取数据而言,这仍然可以节省一些成本。只有当知道数据是只读的或者驱动程序以独占方式控制数据库访问时,这在驱动程序中通常是可行的。
JDBC缓存(如果可用)应该可以通过特定于驱动程序的配置设置进行控制。
编辑:
在第一个查询中,"order by r.validityStart“什么也不做--您可以删除它,一切都会正常工作。
发布于 2013-02-21 16:58:27
查询缓存维护结果,其中查询与参数组合在一起构成了作为标识符的键和值。
来自文档:
当数据高速缓存中没有查询结果中id的任何高速缓存数据时,不使用
不会缓存导致自定义字段类型投影或BigDecimal或BigInteger字段投影的
更可取的是获取整个对象,而不是查询中的字段。
它可能忽略了查询的select部分&缓存结果。后面的部分对于两个查询是相同的,因此产生相同的结果。您可以尝试更改查询执行顺序并观察结果。
发布于 2013-03-11 15:20:49
我相信你的问题不是二级缓存的问题,而是其他的问题。缓存本身不能改变预期的结果。
更有把握的是,在开始第二次查询之前,您可以尝试使用以下代码清除二级缓存:
session.setCacheMode(CacheMode.IGNORE); // session here is the SessionFactory如果问题仍然存在,那么很明显二级缓存不是罪魁祸首。
https://stackoverflow.com/questions/14934948
复制相似问题