Hibernate Cache Annotation区域属性不起作用

时间:2014-02-17 19:37:59

标签: hibernate caching attributes annotations region

我有一个spring / springwebflow(app A)应用程序,它使用Hibernate二级缓存和查询缓存。应用程序只从数据库中读取,而不是添加/更新/删除语句。

查询被缓存到一个名为“daoCache”的区域。我通过namedQuerys

上的注释设置了它

我的实体带有来自Hibernate @Cache的注释,其属性区域也设置为“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分钟的限制来刷新应用A上的缓存区域。因此,应用B中的更改会在20分钟后反映在应用A中。

因此,假设app A执行查询“select * from group_operator where point_master = 1000”。该查询将使用结果标识符保存在缓存中,比如groupOperator#1。因此,该查询执行它的任何其他时间(在20分钟之间)将从缓存返回标识符#1。并且实体也将保存在同一缓存区域中。

问题是当应用B删除记录并创建一个新记录(具有不同的ID)时, *与查询中的参数使用相同的值*

因此,在20分钟之间,应用A将返回实体groupOperator#1,并且应用日志中不会显示任何选择语句。这是预期的行为。

20分钟后,缓存将被重置。

现在App A尝试执行相同的查询(“select * from group_operator where point_master = 1000”)。它将对数据库进行查询,因为缓存在20分钟后被清除。因此结果应该是在App B中创建的新ID。但是我得到的是ObjectNotFoundException,而不存在具有给定标识符的行:groupOperator#1

即使是select语句也显示在日志中,但仍然返回旧的实体标识符。当然,ID 1不再存在于数据库中。

这很奇怪,因为我认为清理缓存会再次执行查询

如果实体和查询都缓存在同一个区域,为什么还在寻找旧实体?

经过几天的调查,使用堆转储,我发现实体没有被缓存在@Cache注释上指定的“daoCache”上。但是正在缓存在名为“xxx.xxx.xxx.xx.groupOperator”的新区域上。似乎该注释的属性不起作用。

我有几个问题

1 - 为什么Cache Annotation中的region属性不起作用?即使我设置“daoCache”还是保存在另一个?这是来自hibernate的错误吗?

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>

使用以下版本的hibernate

<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。这是很好的,缓存查询,即使我删除表中的所有记录仍然返回旧记录而不执行数据库上的任何选择。因此查询缓存效果很好。

问题是:......

删除表上的所有记录并验证查询缓存是否正常后,我在同一个表中添加了一条新记录,其中包含与以前相同的值但不同的主ID

清除缓存后(20分钟后自动),再次通过数据库执行查询(这是正常的,我可以看到日志中的select语句)。找到表中的新记录,并且应该返回一个标识符,然后将整个Entity加载到内存中。奇怪的是,当它试图将整个Entity放入内存时,正在使用旧标识符查找该实体,而不是之前添加的新标识符。就像查询,甚至是在数据库上再次执行一样,仍然返回旧的标识符。但我再说一遍,查询缓存在其他情况下正常工作,这是肯定的。

所以,我不明白查询缓存和实体缓存如何协同工作。就像查询缓存和实体缓存之间的问题一样。我这样说是因为我通过在ehcache.xml中添加该Entity的区域配置来解决这个问题。

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

2 个答案:

答案 0 :(得分:2)

固定!

我终于解决了。 首先,我所做的是启用hibernate和ehcache的所有调试

在日志中搜索我发现数据库模型没有正确配置

Object GroupOperator有一个名为Calendar的属性。 问题是对象Calendar还有一个带有Cache注释的GroupOperator列表。所以每次Hibernate获取新的GroupOperator时也想刷新像Calendar这样的子实体,但是Calendar有自己的GroupOperator列表,并且看起来子实体不在同一个缓存区域或以不同的方式工作。

问题解决了! 感谢您的时间@jhadesdev,有助于继续调查此事。

此致

答案 1 :(得分:0)

您使用的是hibernate.cache.region.factory_classEhCacheRegionFactory还是SingletonEhCacheRegionFactory?这可能与第1点有关。

对于第2点,它可以正常工作,但查询需要单独标记为可缓存。在条件API中,这是使用setCacheable(true)完成的,在JPQL中使用查询提示。

对于两个缓存不同步的问题,查询缓存不一定与实体所在的缓存区域同时被驱逐。

默认情况下,查询默认缓存在默认缓存区域中,但可以选择使用API​​ criteria.setCacheRegion()缓存查询的区域。

通过将查询置于与实体相同的缓存区域中,这将确保同时逐出驱逐查询缓存结果和缓存实体。