2016-02-25 16 views
10

Ich benutze Hibernate 4.3.11.Final mit Spring 3.2.11.RELEASE. Ich bin verwirrt, warum meine Cache-Räumung nicht funktioniert. Ich habe diesen Satz in meinem DAO ...Warum wird meine Entität nicht aus meinem Second-Level-Cache entfernt?

@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); 
} 

und hier ist mein Frühling Config ...

<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> 

Doch im folgenden Test wird meine Einheit nicht aus dem Cache vertrieben zu werden, das weiß ich, weil die Zeile mit „Trefferanzahl # 3:“ druckt „3“, während die Zeile mit „Trefferanzahl # 2:“ druckt ‚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; 
    }); 

Was die richtige Konfiguration ist mir zu erlauben, eine Einheit zu vertreiben. von meiner zweiten le vel Cache

Edit:? Die CacheKeyGenerator Klasse ich in meinem Anwendungskontext verwiesen wird, definiert unter

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; 
    } 
} 

Als solche muss ich einen „Schlüssel“ nicht definieren für jede @Cacheable Anmerkung die ich bevorzugen (weniger Code). Ich weiß jedoch nicht, wie dies für CacheEviction gilt. Ich dachte, die Annotation @CacheEvict würde das gleiche Schlüsselgenerierungsschema verwenden.

Antwort

3

Ihnen fehlen die Cache-Schlüssel für @Cacheable und @CacheEvict. Aus diesem Grund verwenden die beiden Operationen unterschiedliche Cache-Schlüssel und daher wird die Entität nicht entfernt.

Aus der JavaDocs für @Cacheable.key:

Frühling Expression Language (Spel) Ausdruck für den Schlüssel dynamisch berechnet wird. Der Standardwert ist "". Dies bedeutet, dass alle Methodenparameter als Schlüssel betrachtet werden, sofern nicht ein benutzerdefinierter {@link #keyGenerator} konfiguriert wurde.

So bedeutet @Cacheable(value = "main") public Organization findById(String id), dass das zurückgegebene Objekt (vom Typ Organization) wird mit dem Schlüssel id zwischengespeichert werden.

In ähnlicher Weise bedeutet @Caching(evict = { @CacheEvict("main") }) public Organization save(Organization organization), dass die Zeichenfolgendarstellung organization als Cache-Schlüssel betrachtet wird.


Die Lösung ist folgende Änderungen vornehmen:

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

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

Das die beiden Cache-Operationen zwingen wird, den gleichen Schlüssel zu verwenden.

+0

Weil ich dies in meinem Anwendungskontext (von meiner Frage), ' Dave

+0

Ihr Schlüsselgenerator generiert unterschiedliche Schlüssel für die beiden Methoden, was wiederum zu einer falschen Schlüsselübereinstimmung und somit zu keiner Räumung führt. Aktivieren Sie Debug-Protokolle, um dies zu überprüfen. Sie werden Spring-Caching-Protokolle sehen, die Ihnen den logischen Fehler bei der Generierung des Schlüssels anzeigen. – manish

+0

Ich habe eine [Beispielanwendung] (https://github.com/manish-in-java/stackoverflow-questions/tree/master/35640220) erstellt, um Ihnen zu demonstrieren, dass meine Antwort funktioniert. Sie können es herunterladen und es als 'mvn clean test' ausführen, um alle Tests zu sehen. Es gibt einen Test, um den Cache-Status entsprechend Ihrer Anrufe zu überprüfen. Ich schlage vor, dass Sie mein Beispiel nehmen und Ihren Code hinzufügen, ohne Ihren benutzerdefinierten Schlüsselgenerator zuerst. Die Dinge sollten funktionieren, wenn Sie keine Änderungen am Arbeitsprobe vornehmen. Dann stecken Sie Ihren Schlüsselgenerator ein, um zu sehen, wo der Fehler ist. – manish

4

Ich habe die CodeKeyGenerator wie folgt umgeschrieben. Dadurch wird basierend auf dem von Ihnen gesendeten Parameter ein Schlüssel erstellt. Wenn es eine Zeichenfolge ist (im Falle von ID), wird es so verwendet, wie es ist. Wenn es ein Organization Objekt ist, erhält es die ID von diesem Objekt und verwendet es für den Schlüssel. Auf diese Weise müssen Sie Ihren Code nicht überall neu schreiben. (Nur ändern müssen Sie Ihre CacheKeyGenerator durch den folgenden Code ersetzen.)

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

Also ich verstehe deine Antwort, du sagst, ich kann jeder Cacheable-Annotation in meiner Anwendung einen "Schlüssel" hinzufügen (keine Option, da ich einen automatisch generierten Schlüssel möchte) oder ich kann die Signatur jeder Methode in meinem ändern Anwendung mit Cacheable (zB change save (organizaiton org) zum speichern (param1, param2, ...)? Ich verstehe dich richtig? – Dave

+0

Ja du hast es. – Thanga

+0

Es ist keine Option, jede Methodensignatur in meinem Code neu zu codieren Anwendung, die eine Cacheable-Annotation verwendet - es gibt Hunderte.Wie würde ich eine @CacheEvict-Annotation mit meiner aktuellen Methodensignatur und meinem Schlüsselgenerator schreiben? – Dave

1

Was Sie gewaltsam zu vertreiben ist nicht ein Hibernate Second-Level-Cache, sondern ein Spring Cache, das ist völlig anders als Caching-Schicht versuchen.

Wie pro docs des Ruhezustandes, Cache der zweiten Ebene ist eine Cluster oder JVM-Ebene (Sessionebene) Cache auf einer Klasse-für-Klasse und Sammel-by-Sammlung Basis.

Das bedeutet, es wird nur durch einen Hibernate verwaltet und Annotationen wie @Cacheable oder @CacheEvict haben keinen Einfluss darauf.

Es ist nicht besonders klar, wie Sie die m_cache-Instanz in Ihrem Test erhalten, aber vorausgesetzt, es handelt sich wirklich um den Second-Level-Cache von Hibernate, wird er nicht mit den von Ihnen verwendeten Anmerkungen entfernt.

Sie müssen es programmatisch vertreiben, zum Beispiel:

sessionFactory.evict(Organization.class) 

Wie auch immer, solange Sie innerhalb der einzelnen JVM alle Ihre Daten zugreifen zu tun und durch die Hibernate, sollten Sie nicht über Cache-Räumung Sorge, es wird vom Framework selbst transparent gehandhabt.

Weitere Informationen zu den Räumungsmöglichkeiten finden Sie in der Hibernate-Dokumentation, Kapitel 20.3. Verwalten der Caches.

+0

Dies ist eine verteilte Anwendung, die eine JVM pro Anwendungsserver verwendet und Spring und ehcache als Cache der zweiten Ebene verwendet. Wie würde ich die entsprechende @ CacheEvict Annotation angesichts der Einschränkungen meiner Frage schreiben? Die Lösung gilt für die Annotation "org.springframework.cache.annotation.CacheEvict". – Dave

+0

Können Sie den Code wie Sie 'm_cache' Instanz abrufen? Momentan hat Ihre Anwendung ** zwei ** Caching-Layer. Einer ist der Spring Cache (der die Aufrufe an Ihr DAO zwischenspeichert), der zweite ist der eigentliche Second-Level-Cache von Hibernate. Wenn es nicht bekannt ist, welche Art von Cache 'm_cache' ist, ist es schwer zu irgendeiner Bewertung. –

+0

Ich habe die Definition in der Frage veröffentlicht. "m_cache" hat den Typ "net.sf.ehcache.Cache". – Dave