Сбой гибернации 2-го уровня, когда другой процесс изменяет базу данных
У нас есть приложение, использующее кэширование второго уровня Hibernate, чтобы избежать попадания в базу данных.
Мне было интересно, есть ли простой способ аннулировать кэш-память второго уровня Java Hibernate второго уровня, когда внешний процесс, такой как администратор MySQL, напрямую связан с изменением базы данных (обновление/вставка/удаление).
Мы используем EHCache в качестве нашей реализации кэша второго уровня.
Мы используем сочетание @Cache (использование = CacheConcurrencyStrategy.READ_WRITE) и @Cache (использование = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE), и у нас нет оптимистичного управления concurrency с использованием временных меток для каждого объекта.
SessionFactory содержит методы управления кешем второго уровня:
- Управление кэшами
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
Но поскольку мы аннотируем отдельные классы сущностей с помощью @Cache, нет никакого центрального места для нас, чтобы "надежно" (например, никаких ручных шагов) добавить это в список.
// 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);
}
}
Нет реального способа для кэша второго уровня Hibernate знать, что сущность изменена в БД, если она не запрашивает этот объект (из которого кеш защищает вас). Поэтому, возможно, в качестве решения мы могли бы просто вызвать какой-либо метод, чтобы заставить кеш второго уровня вытеснять все (опять же из-за отсутствия блокировки и управления concurrency вы рискуете совершать транзакции из "чтения" или обновления устаревших данных).
Ответы
Ответ 1
На основе комментариев ChssPly76 здесь приведен метод, который вытесняет все сущности из кеша второго уровня (мы можем предоставить этот метод администраторам через JMX или другие средства администрирования):
/**
* 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);
}
}
Ответ 2
SessionFactory имеет множество методов evict()
именно для этой цели:
sessionFactory.evict(MyEntity.class); // remove all MyEntity instances
sessionFactory.evict(MyEntity.class, new Long(1)); // remove a particular MyEntity instances
Ответ 3
Оба спящего режима и JPA теперь обеспечивают прямой доступ к базовому кэшу второго уровня:
sessionFactory.getCache().evict(..);
entityManager.getCache().evict(..)
Ответ 4
Я искал, как сделать недействительными все кэши Hibernate, и я нашел этот полезный фрагмент:
sessionFactory.getCache().evictQueryRegions();
sessionFactory.getCache().evictDefaultQueryRegion();
sessionFactory.getCache().evictCollectionRegions();
sessionFactory.getCache().evictEntityRegions();
Надеюсь, что это поможет кому-то другому.
Ответ 5
Вы можете попробовать:
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;
}
Надеюсь, это поможет.
Ответ 6
При использовании распределенного кэша следует учитывать одну вещь: QueryCache является локальным, и его удаление на одном узле не исключает его на другом. Другая проблема заключается в том, что удаление области Entity без исключения области Query приведет к выбору N + 1 при попытке получить дату из кэша Query. Хорошие чтения по этой теме здесь.