database - example - hibernate-ehcache maven
Hibernar la invalidación de caché de segundo nivel cuando otro proceso modifica la base de datos (5)
Tenemos una aplicación que utiliza el almacenamiento en caché de segundo nivel de Hibernate para evitar los accesos a la base de datos.
Me preguntaba si hay alguna manera fácil de invalidar el caché de segundo nivel Hibernate de la aplicación Java cuando un proceso externo, como un administrador de MySQL, se conecta directamente para modificar la base de datos (actualizar / insertar / eliminar).
Estamos utilizando EHCache como nuestra implementación de caché de segundo nivel.
Usamos una combinación de @Cache (use = CacheConcurrencyStrategy.READ_WRITE) y @Cache (use = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE), y no tenemos el control de concurrencia Optimista habilitado usando marcas de tiempo en cada entidad.
El SessionFactory contiene métodos para administrar el caché de segundo nivel: - Administrar 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 las clases de entidades individuales con @Cache, no hay un lugar central para que podamos "confiablemente" (por ejemplo, sin pasos manuales) agregar 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 una manera real para que el caché de segundo nivel de Hibernate sepa que una entidad cambió en la base de datos a menos que consulte a esa entidad (que es de lo que el caché lo protege). Entonces, tal vez como una solución, podríamos simplemente llamar a algún método para forzar a la memoria caché de segundo nivel a desalojarlo todo (nuevamente, debido a la falta de bloqueo y al control de concurrencia, se corre el riesgo de que las transacciones se "lean" o actualicen datos obsoletos).
Basado en ChssPly76''s comentarios ChssPly76''s aquí hay un método que desaloja a todas las entidades de la caché de segundo nivel (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);
}
}
Estaba buscando cómo invalidar todos los cachés de Hibernate y encontré este fragmento de código útil:
sessionFactory.getCache().evictQueryRegions();
sessionFactory.getCache().evictDefaultQueryRegion();
sessionFactory.getCache().evictCollectionRegions();
sessionFactory.getCache().evictEntityRegions();
Espero que ayude a alguien más.
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.
Tanto hibernate como JPA ahora brindan acceso directo al caché de segundo nivel subyacente:
sessionFactory.getCache().evict(..);
entityManager.getCache().evict(..)
SessionFactory tiene muchos métodos de 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