Domanda

Abbiamo un'applicazione che utilizza la memorizzazione nella cache di 2 ° livello di Hibernate per evitare accessi al database.

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

Stiamo utilizzando EHCache come implementazione della cache di 2 ° livello.

Usiamo un mix di @Cache (use = CacheConcurrencyStrategy.READ_WRITE) e @Cache (use = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE) e non abbiamo il controllo di concorrenza ottimistico abilitato utilizzando i timestamp su ogni entità.

SessionFactory contiene metodi per gestire la cache di 2 ° livello:  - Gestione delle cache

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 poiché annotiamo le singole classi di entità con @Cache, non c'è posto centrale per noi per "quotare in modo affidabile" (ad es. nessun passaggio manuale) aggiungilo all'elenco.

// 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'è modo reale per la cache di 2 ° livello di Hibernate di sapere che un'entità è cambiata nel DB a meno che non interroghi quell'entità (che è ciò da cui la cache ti protegge). Quindi forse come soluzione potremmo semplicemente chiamare un metodo per forzare la cache di secondo livello a sfrattare tutto (di nuovo a causa della mancanza di blocco e controllo della concorrenza si rischia nelle transazioni in corso da "leggere" o aggiornare dati non aggiornati).

È stato utile?

Soluzione

Basato sui commenti di ChssPly76 ecco un metodo che sfrutta tutte le entità dalla cache di 2 ° livello (possiamo esporre questo metodo agli amministratori tramite JMX o altri strumenti di amministrazione):

/**
 * 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);
    }
}

Altri suggerimenti

SessionFactory ha un sacco di evict () metodi precisamente per questo scopo:

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

Sia l'ibernazione che l'APP ora forniscono l'accesso diretto alla cache di secondo livello sottostante:

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

Stavo cercando come invalidare tutte le cache Hibernate e ho trovato questo utile frammento:

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

Spero che aiuti a qualcun altro.

Puoi provare a farlo:

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 sia d'aiuto.

Una cosa da tenere in considerazione quando si utilizza la cache distribuita è che QueryCache è locale e sfrattarlo su un nodo non lo sfrutta da un altro. Un altro problema è: lo sfratto dell'area Entity senza lo sfratto dell'area Query causerà selezioni N + 1, quando si tenta di recuperare la data dalla cache delle query. Buone letture su questo argomento qui .

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top