Hibernar invalidación de caché de 2do nivel cuando otro proceso modifica la base de datos


Tenemos una aplicación que utiliza el almacenamiento en caché de 2do nivel de Hibernate para evitar accesos a la base de datos.

Me preguntaba si hay alguna manera fácil de invalidar la caché de 2nd nivel de Hibernación de la aplicación Java cuando un proceso externo como un administrador de MySQL se conecta directamente para modificar la base de datos (update/insert/delete).

Estamos usando EHCache como nuestra implementación de caché de 2do nivel.

Usamos una mezcla de @Cache(usage = CacheConcurrencyStrategy.READ_WRITE) y @Cache (usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE), y no tenemos el control de concurrencia optimista habilitado usando marcas de tiempo en cada entidad.

La SessionFactory contiene métodos para administrar la caché de 2do nivel: - Gestionar los Cachés

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

Pero debido a que anotamos clases de entidades individuales con @Cache, no hay un lugar central para que "de manera confiable" (por ejemplo, sin pasos manuales) agreguemos eso a la 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);
   }
}

No hay manera real para Hibernate 2nd level cache para saber que una entidad cambió en la base de datos a menos que consulte a esa entidad(que es de lo que la caché le está protegiendo). Así que tal vez como una solución podríamos simplemente llamar a algún método para forzar la caché de segundo nivel para desalojar todo (de nuevo debido a la falta de bloqueo y control de concurrencia se corre el riesgo de transacciones en curso de "lectura" o actualización de datos obsoletos).

Author: Dougnukem, 2009-10-22

5 answers

Basado en los comentarios de de ChssPly76 aquí hay un método que desaloja a todas las entidades de la caché de 2nd level (podemos exponer este método a los administradores a través de JMX u otras herramientas de administración):

/**
 * 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);
    }
}
 14
Author: Dougnukem,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 11:46:42

SessionFactory tiene muchos métodos evict() precisamente para ese propósito:

sessionFactory.evict(MyEntity.class); // remove all MyEntity instances
sessionFactory.evict(MyEntity.class, new Long(1)); // remove a particular MyEntity instances
 11
Author: ChssPly76,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-10-21 22:00:23

Tanto hibernate como JPA ahora proporcionan acceso directo a la caché de nivel 2 subyacente:

sessionFactory.getCache().evict(..);
entityManager.getCache().evict(..)
 8
Author: Bozho,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-05-15 09:01:23

Estaba buscando cómo invalidar todas las cachés de Hibernación y encontré este fragmento útil:

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

Espero que ayude a alguien más.

 2
Author: jelies,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-03-11 10:03:08

Puedes intentar hacer esto:

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

Espero que ayude.

 1
Author: Aalkhodiry,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-05-28 09:02:31