2016-02-25 61 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

相關問題