2009-10-21 10 views
22

Abbiamo un'applicazione che utilizza il caching di secondo livello di Hibernate per evitare gli hit del database.Sospensione dell'invalidazione della cache di secondo livello quando un altro processo modifica il database

Mi chiedevo se esiste un modo semplice per invalidare la cache di secondo livello di Hibernate dell'applicazione Java quando un processo esterno come un amministratore MySQL è connesso direttamente per modificare il database (aggiornamento/inserimento/eliminazione).

Stiamo utilizzando EHCache come implementazione della cache di secondo livello.

Abbiamo utilizzare una combinazione di @Cache (utilizzo = CacheConcurrencyStrategy.READ_WRITE) e @Cache (utilizzo = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE), e noi non hanno il controllo della concorrenza ottimistica attivata utilizzando timestamp su ogni entità.

Il SessionFactory contiene i metodi per gestire la cache di 2 ° livello: - Managing the Caches

sessionFactory.evict(Cat.class, catId); //evict a particular Cat 
sessionFactory.evict(Cat.class); //evict all Cats 
sessionFactory.evictCollection("Cat.kittens", catId); //evict a particular collection of kittens 
sessionFactory.evictCollection("Cat.kittens"); //evict all kitten collections 

Ma perché viene annotata singole classi di entità con @Cache, non c'è posto centrale per noi (ad esempio, nessun manuale "affidabile" passi) aggiungilo alla lista.

// Easy to forget to update this to properly evict the class 
public static final Class[] cachedEntityClasses = {Cat.class, Dog.class, Monkey.class} 

public void clear2ndLevelCache() { 
    SessionFactory sessionFactory = ... //Retrieve SessionFactory 

    for (Class entityClass : cachedEntityClasses) { 
     sessionFactory.evict(entityClass); 
    } 
} 

Non c'è nessun vero modo per la cache di 2 ° livello di Hibernate per sapere che un soggetto ha cambiato nel DB a meno che non interroga quell'entità (che è ciò che la cache è che si protegge da). Quindi, forse come soluzione, potremmo semplicemente chiamare un metodo per forzare la cache di secondo livello a sfrattare tutto (sempre a causa della mancanza di blocco e del controllo della concorrenza si rischiano transazioni in corso da "leggere" o aggiornare dati obsoleti).

risposta

14

Sulla base ChssPly76's commenti Ecco un metodo che espelle tutte le entità dalla cache di 2 ° livello (siamo in grado di esporre questo metodo per gli amministratori attraverso JMX o altro amministratore strumenti):

/** 
* Evicts all second level cache hibernate entites. This is generally only 
* needed when an external application modifies the game databaase. 
*/ 
public void evict2ndLevelCache() { 
    try { 
     Map<String, ClassMetadata> classesMetadata = sessionFactory.getAllClassMetadata(); 
     for (String entityName : classesMetadata.keySet()) { 
      logger.info("Evicting Entity from 2nd level cache: " + entityName); 
      sessionFactory.evictEntity(entityName); 
     } 
    } catch (Exception e) { 
     logger.logp(Level.SEVERE, "SessionController", "evict2ndLevelCache", "Error evicting 2nd level hibernate cache entities: ", e); 
    } 
} 
+0

Desidero cancellare i dati della cache dalla cache di secondo livello chiamando il metodo seguente: - sessionFactory.getCache(). EvictEntityRegions(); Voglio solo sapere, c'è qualche danno nel fare questo? Ad esempio: - Cosa succederà se provo a svuotare la cache nel mezzo della transazione? –

11

SessionFactory ha un sacco di evict() metodi appunto a tale scopo:

sessionFactory.evict(MyEntity.class); // remove all MyEntity instances 
sessionFactory.evict(MyEntity.class, new Long(1)); // remove a particular MyEntity instances 
+0

Esiste un metodo affidabile per rimuovere tutte le istanze di tutte le classi annotate con @Cache dalla cache di secondo livello. Annotiamo generalmente le classi per abilitare la cache di secondo livello e quindi non c'è davvero una posizione centrale per aggiornare il codice quando una nuova entità viene aggiunta alla cache di secondo livello. – Dougnukem

+1

Per essere onesti, sfrattare tutte le ** istanze ** sembra un caso piuttosto estremo. Fatelo abbastanza spesso e potreste finire con prestazioni peggiori che senza cache. Detto questo, puoi ottenere i nomi di tutte le entità tramite il metodo 'getAllClassMetadata()' (i nomi sono chiavi della mappa restituita) e chiama 'evictEntity()' per ciascuno. – ChssPly76

+0

Questa è solo un'occasione estremamente rara quando è necessario eseguire una produzione "HOTFIX" del database senza arrestare i server e gli strumenti di amministrazione del servizio clienti esistenti non possono farlo (e preferiremmo non esporre qualche comando di amministrazione che ti permette di eseguire alcuni SQL arbitrari). Quindi, di nuovo consentendo a un amministratore esterno di eseguire SQL, perdiamo tutto il controllo e la registrazione delle transazioni. – Dougnukem

8

Sia hibernate e JPA ora forniscono accesso diretto alla cache di 2 ° livello sottostante:

sessionFactory.getCache().evict(..); 
entityManager.getCache().evict(..) 
2

stavo cercando il modo di invalidare tutte le cache di Hibernate e ho trovato questo frammento utile:

sessionFactory.getCache().evictQueryRegions(); 
sessionFactory.getCache().evictDefaultQueryRegion(); 
sessionFactory.getCache().evictCollectionRegions(); 
sessionFactory.getCache().evictEntityRegions(); 

Speranza che aiuta a qualcun altro.

1

Si può provare a fare questo:

private EntityManager em; 

public void clear2ndLevelHibernateCache() { 
    Session s = (Session) em.getDelegate(); 
    SessionFactory sf = s.getSessionFactory(); 

    sf.getCache().evictQueryRegions(); 
    sf.getCache().evictDefaultQueryRegion(); 
    sf.getCache().evictCollectionRegions(); 
    sf.getCache().evictEntityRegions(); 

    return; 
} 

spero che aiuta.

Problemi correlati