2016-02-25 45 views
10

我在Spring 3.2.11.RELEASE中使用Hibernate 4.3.11.Final。我很困惑,为什么我的缓存驱逐不起作用。我有这个成立于我的DAO ...为什么我的实体不会从我的二级缓存中被驱逐?

@Override 
@Caching(evict = { @CacheEvict("main") }) 
public Organization save(Organization organization) 
{ 
    return (Organization) super.save(organization); 
} 

@Override 
@Cacheable(value = "main") 
public Organization findById(String id) 
{ 
    return super.find(id); 
} 

,这里是我的Spring配置...

<cache:annotation-driven key-generator="cacheKeyGenerator" /> 

<bean id="cacheKeyGenerator" class="org.mainco.subco.myproject.util.CacheKeyGenerator" /> 

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

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

<util:map id="jpaPropertyMap"> 
    <entry key="hibernate.show_sql" value="true" /> 
    <entry key="hibernate.dialect" value="org.mainco.subco.myproject.jpa.subcoMysql5Dialect" /> 
    <entry key="hibernate.cache.region.factory_class" value="org.hibernate.cache.ehcache.EhCacheRegionFactory" /> 
    <entry key="hibernate.cache.provider_class" value="org.hibernate.cache.EhCacheProvider" /> 
    <entry key="hibernate.cache.use_second_level_cache" value="true" /> 
    <entry key="hibernate.cache.use_query_cache" value="false" /> 
    <entry key="hibernate.generate_statistics" value="true" /> 
    <entry key="javax.persistence.sharedCache.mode" value="ENABLE_SELECTIVE" /> 
</util:map> 

<bean id="sharedEntityManager" 
    class="org.springframework.orm.jpa.support.SharedEntityManagerBean"> 
    <property name="entityManagerFactory" ref="entityManagerFactory" /> 
</bean> 
在下面的测试

然而,我的实体不获取从缓存中逐出,我知道是因为与“命中次数#3:”行,打印出“3”,而与“命中次数#2:”行打印出‘2’

private net.sf.ehcache.Cache m_cache 

@Autowired 
private net.sf.ehcache.CacheManager ehCacheManager; 

@Before 
public void setup() 
{ 
    m_cache = ehCacheManager.getCache("main"); 
    m_transactionTemplate = new TransactionTemplate(m_transactionManager); 
} // setup 

... 
@Test 
public void testCacheEviction() 
{ 
    final String orgId = m_testProps.getProperty("test.org.id"); 

    // Load the entity into the second-level cache 
    m_transactionTemplate.execute((TransactionCallback<Void>) transactionStatus -> {    
     m_orgSvc.findById(orgId); 
     return null; 
    }); 

    final long hitCount = m_cache.getStatistics().getCacheHits(); 
    System.out.println("hit count #1:" + hitCount); 
    m_transactionTemplate.execute((TransactionCallback<Void>) transactionStatus -> {    
     final Organization org = m_orgSvc.findById(orgId); 
     System.out.println("hit count:" + m_cache.getStatistics().getCacheHits()); 
     org.setName("newName"); 
     m_orgSvc.save(org); 
     return null; 
    }); 

    // Reload the entity. This should not incur a hit on the cache. 
    m_transactionTemplate.execute((TransactionCallback<Void>) transactionStatus -> { 
     System.out.println("hit count #2:" + m_cache.getStatistics().getCacheHits()); 
     final Organization newOrg = m_orgSvc.findById(orgId); 
     System.out.println("hit count #3:" + m_cache.getStatistics().getCacheHits()); 
     return null; 
    }); 

什么是正确的配置让我来赶一个实体。来自我的第二乐队VEL缓存

编辑:的CacheKeyGenerator类我在应用程序上下文中引用如下

public class CacheKeyGenerator implements KeyGenerator 
{ 

    @Override 
    public Object generate(final Object target, final Method method, 
     final Object... params) { 

     final List<Object> key = new ArrayList<Object>(); 
     key.add(method.getDeclaringClass().getName()); 
     key.add(method.getName()); 

     for (final Object o : params) { 
      key.add(o); 
     } 
     return key; 
    } 
} 

定义的那样,我没有定义每个@Cacheable注释的“钥匙”,我更喜欢(少代码)。但是,我不知道这是如何适用于CacheEviction。我认为@CacheEvict注释会使用相同的密钥生成方案。

回答

3

您缺少@Cacheable@CacheEvict的缓存键。因此,这两个操作使用不同的缓存键,因此实体不会被驱逐。

从JavaDoc中@Cacheable.key:用于动态地计算所述密钥

弹簧表达式语言(使用SpEL)的表达。默认值为"",这意味着除非已配置自定义{@link #keyGenerator},否则将所有方法参数视为关键字。

所以,@Cacheable(value = "main") public Organization findById(String id)意味着返回的对象(的Organization型)将与键id被缓存。

类似地,@Caching(evict = { @CacheEvict("main") }) public Organization save(Organization organization)意味着organization的字符串表示形式将被视为缓存键。


解决的办法是做如下修改:

@Cacheable(value = "main", key ="#id) 

@CacheEvict(value = "main", key = "#organization.id") 

这将迫使二级缓存操作使用相同的密钥。

+0

因为我在我的应用程序上下文中包含了这个(从我的问题),' Dave

+0

您的密钥生成器会为两种方法生成不同的密钥,这又会导致密钥不匹配,因此不会驱逐。打开调试日志来验证这一点。你会看到Spring缓存日志,它会告诉你如何生成密钥的逻辑错误。 – manish

+0

我已经创建了一个[示例应用程序](https://github.com/manish-in-java/stackoverflow-questions/tree/master/35640220)向您展示我的答案有效。你可以下载并运行它作为'mvn clean test'来查看所有的测试通过。有一个测试在那里根据你的调用来检查缓存状态。我会建议你拿我的样品,并添加你的代码,没有你的自定义密钥生成器第一。如果您不对工作样本进行任何更改,事情就应该起作用。然后插入密钥生成器以查看错误的位置。 – manish

4

我重写了如下的CodeKeyGenerator。这将根据您发送的参数进行设置。如果它是一个字符串(在ID的情况下),它会照原样使用它。如果它是一个Organization对象,它将从该对象获取该ID并将其用于该键。这样你就不需要在所有地方重写你的代码。 (只有更改是您需要用以下代码替换CacheKeyGenerator。)

public class CacheKeyGenerator implements KeyGenerator 
{ 
    @Override 
    public Object generate(final Object target, final Method method, 
     final Object... params) { 
    StringBuilder sb = new StringBuilder(); 
    sb.append(o.getClass().getName()); 
    sb.append(method.getName()); 

    if (params[0].getClass().getName() == "Organization") { 
     sb.append(((Organization) params[0]).id); 
    } 
    else if (params[0].getClass().getName() == "java.lang.String") { 
     sb.append(params[0].toString()); 
    } 
    return sb.toString(); 
    } 
} 

+0

所以我明白你的答案,你说我可以在我的应用程序中为每个Cacheable注释添加一个“密钥”(因为我想要一个自动生成的密钥,所以没有选项),或者我可以更改我的每个方法的签名应用程序与缓存(例如更改保存(organizaiton org)保存(param1,param2,...)?我的理解正确吗? – Dave

+0

是的,你知道了 – Thanga

+0

这是不是一个选项重新编码我的每个方法签名使用Cacheable注解的应用程序 - 有数百个,我将如何编写@CacheEvict注释,给定我当前的方法签名和密钥生成器? – Dave

1

你试图驱逐什么是不一个Hibernate的二级缓存,而是Spring Cache,这是完全不同的缓存层。

作为每Hibernate的docs,第二级高速缓存是一类逐类和集合逐收集基础一个群集或JVM级别(SessionFactory级)高速缓存。

这意味着它仅由Hibernate进行管理,并且诸如@Cacheable@CacheEvict的注释对其没有影响。

如何在测试中获得m_cache实例并不是特别清楚,但假设它是Hibernate的二级缓存,它不会被使用注释所驱逐。

你必须以编程方式驱逐它,例如:

sessionFactory.evict(Organization.class) 

反正,只要你做一个JVM中,并通过Hibernate所有的数据访问,你不应该担心高速缓存收回,它由框架本身透明地处理。

欲了解更多关于驱逐的可能性,请参阅Hibernate文档,章节20.3。管理缓存

+0

这是一个分布式应用程序,它将为每个应用程序服务器使用一个JVM,并将Spring和ehcache用作二级缓存。考虑到我的问题的限制,我将如何编写相应的@CacheEvict注释?解决方案shoudl适用于“org.springframework.cache.annotation.CacheEvict”注释。 – Dave

+0

你可以发布代码如何检索'm_cache'实例吗?目前你的应用程序有**两个**缓存层。一个是Spring Cache(缓存对DAO的调用),另一个是Hibernate的实际二级缓存。除非知道什么样的缓存“m_cache”,否则很难进行任何评估。 –

+0

我已经在问题中发布了定义。 “m_cache”的类型是“net.sf.ehcache.Cache”。 – Dave

相关问题