2016-02-25 16 views
10

Sto usando Hibernate 4.3.11.Final con Spring 3.2.11.RELEASE. Sono confuso sul motivo per cui il mio sfratto della cache non funziona. Ho questo set up della mia DAO ...Perché la mia entità non è sfrattata dalla mia cache di secondo livello?

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

ed ecco la mia primavera 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> 

Eppure, nel test di seguito, il mio soggetto non sta ottenendo sfrattato dalla cache, che so, perché la linea con “numero di passaggi # 3:” stampa i “3”, mentre la linea con "numero di passaggi # 2:” stampa out ‘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; 
    }); 

Qual è la giusta configurazione per permettermi di sfrattare un'entità. dal mio secondo-le cache di vel

Edit:? La classe CacheKeyGenerator ho fatto riferimento nel mio contesto di applicazione è definito di seguito

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

Come tale non c'è bisogno di definire una “chiave” per ciascuna annotazione @Cacheable che ho preferisci (meno codice). Tuttavia, non so come questo si applica a CacheEviction. Pensavo che l'annotazione @CacheEvict avrebbe utilizzato lo stesso schema di generazione di chiavi.

risposta

3

Ti mancano le chiavi di cache per entrambi @Cacheable e @CacheEvict. Per questo motivo, le due operazioni utilizzano diverse chiavi di cache e quindi l'entità non viene espulsa.

Dalle JavaDocs per @Cacheable.key:

Primavera Expression Language (SPEL) espressione per calcolare la chiave in modo dinamico. L'impostazione predefinita è "", ovvero tutti i parametri del metodo sono considerati come una chiave, a meno che non sia stato configurato un {@link #keyGenerator} personalizzato.

Quindi, @Cacheable(value = "main") public Organization findById(String id) significa che l'oggetto restituito (di tipo Organization) verrà memorizzata nella cache con la chiave id.

Analogamente, @Caching(evict = { @CacheEvict("main") }) public Organization save(Organization organization) significa che la rappresentazione di stringa di organization verrà considerata come chiave di cache.


La soluzione è quello di apportare le seguenti modifiche:

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

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

Questo forzerà le due operazioni di cache di utilizzare la stessa chiave.

+0

È possibile includerlo nel contesto dell'applicazione (dalla mia domanda), ' Dave

+0

Il generatore di chiavi genera chiavi diverse per i due metodi, che di nuovo determinano una mancata corrispondenza delle chiavi e quindi nessuno sfratto. Attiva i log di debug per verificarlo. Verranno visualizzati i registri di memorizzazione nella cache di Spring che mostreranno l'errore logico nel modo in cui si genera la chiave. – manish

+0

Ho creato una [applicazione di esempio] (https://github.com/manish-in-java/stackoverflow-questions/tree/master/35640220) per dimostrare che la mia risposta funziona. Puoi scaricarlo ed eseguirlo come 'mvn clean test' per vedere tutti i test che passano. C'è un test in là per controllare lo stato della cache in base alle tue chiamate. Ti suggerirò di prendere il mio campione e aggiungervi il codice, senza prima creare il tuo generatore di chiavi personalizzato. Le cose dovrebbero funzionare se non si apportano modifiche al campione funzionante. Quindi collegare il generatore di chiavi per vedere dove si trova l'errore. – manish

4

Ho riscritto lo CodeKeyGenerator come di seguito. Questo farà una chiave in base al parametro che invii. Se è una stringa (in caso di id), la userà così com'è. Se si tratta di un oggetto Organization, ottiene l'ID da quell'oggetto e lo utilizza per la chiave. In questo modo non è necessario riscrivere il codice in tutti i luoghi. (Solo modifica è necessario sostituire il CacheKeyGenerator con il codice riportato di seguito.)

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

Quindi capisco la tua risposta, stai dicendo che posso aggiungere una "chiave" ad ogni annotazione memorizzabile nella mia applicazione (non un'opzione dal momento che voglio una chiave generata automaticamente) o posso cambiare la firma di ogni metodo nella mia applicazione con Cacheable (ad es. modifica save (organizaiton org) da salvare (param1, param2, ...)? Sto comprendendo correttamente? – Dave

+0

Sì. – Thanga

+0

Non è un'opzione per ricodificare ogni firma di metodo nel mio applicazione che utilizza un'annotazione memorizzabile in Cache - ce ne sono centinaia Come potrei scrivere un'annotazione @CacheEvict data la mia attuale firma del metodo e generatore di chiavi? – Dave

1

cosa si sta cercando di sfrattare non è cache del Hibernate di secondo livello, ma piuttosto un Spring Cache, che è completamente diverso livello di cache.

Come per la cache docs, secondo livello di Hibernate è una cache cluster o di livello di JVM (SessionFactory-level) su base classe per classe e la raccolta-by-raccolta.

Ciò significa che è gestito esclusivamente da un Hibernate e annotazioni come @Cacheable o @CacheEvict non hanno alcun effetto su di esso.

Non è particolarmente chiaro come si ottiene l'istanza m_cache nel test, ma se si tratta di una cache di secondo livello di Hibernate, non verrà sfrattata utilizzando le annotazioni utilizzate.

Dovrete sfrattare programatically, ad esempio:

sessionFactory.evict(Organization.class) 

In ogni caso, fino a quando si fare tutto il vostro accesso ai dati all'interno di un'unica JVM e attraverso la sospensione, non si deve preoccupare di cache di sfratto, si è gestito dal framework stesso in modo trasparente.

Per ulteriori informazioni sulle possibilità di sfratto, consultare la documentazione di Hibernate, capitolo 20.3. Gestire le cache.

+0

Questa è un'applicazione distribuita che utilizzerà un server JVM per applicazione e utilizzerà Spring e ehcache come cache di secondo livello. Come scrivere l'annotazione @CacheEvict appropriata in base ai limiti della mia domanda? La soluzione shoudl si applica all'annotazione "org.springframework.cache.annotation.CacheEvict". – Dave

+0

Puoi postare il codice come recuperi l'istanza di 'm_cache'? Attualmente la tua applicazione ha ** due ** livelli di memorizzazione nella cache. Uno è la Spring Cache (che memorizza nella cache le chiamate al tuo DAO), la seconda è l'effettiva cache di secondo livello di Hibernate. A meno che non si conosca il tipo di cache 'm_cache', è difficile fare una valutazione. –

+0

Ho inserito la definizione nella domanda. "m_cache" è di tipo "net.sf.ehcache.Cache". – Dave

Problemi correlati