Question

Nous avons une application qui utilise la mise en cache de second niveau d’Hibernate pour éviter les hits de la base de données.

Je me demandais s'il existait un moyen simple d'invalider le cache de niveau 2 Hibernate de l'application Java lorsqu'un processus externe, tel qu'un administrateur MySQL directement connecté, permettait de modifier la base de données (update / insert / delete).

Nous utilisons EHCache comme implémentation du cache de second niveau.

Nous utilisons un mélange de @Cache (usage = CacheConcurrencyStrategy.READ_WRITE) et @Cache (usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE) et nous ne disposons pas du contrôle de simultanéité Optimistic activé en utilisant des horodatages sur chaque entité.

La SessionFactory contient des méthodes pour gérer le cache de second niveau:  - Gestion des 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

Mais comme nous annotons des classes d'entités individuelles avec @Cache, nous ne disposons pas d'un emplacement central pour "fiables". (par exemple, aucune étape manuelle) ajoutez-le à la liste.

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

Le cache de second niveau d'Hibernate ne dispose d'aucun moyen réel de savoir qu'une entité a changé dans la base de données à moins qu'elle interroge cette entité (ce dont le cache vous protège). Alors peut-être que la solution pourrait tout simplement appeler une méthode pour forcer le cache de second niveau à tout expulser (encore une fois, en raison du manque de verrouillage et du contrôle de la simultanéité, vous risquez que des transactions en cours proviennent de la "lecture" ou de la mise à jour de données obsolètes).

Était-ce utile?

La solution

Sur la base des commentaires de ChssPly76 , voici une méthode qui supprime toutes les entités du cache de niveau 2 (nous pouvons exposer cette méthode aux administrateurs via JMX ou d’autres outils d’administration):

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

Autres conseils

SessionFactory contient beaucoup de evict () précisément à cette fin:

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

Hibernate et JPA fournissent désormais un accès direct au cache de second niveau sous-jacent:

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

Je cherchais comment invalider tous les caches Hibernate et j'ai trouvé cet extrait utile:

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

J'espère que cela aidera quelqu'un d'autre.

Vous pouvez essayer ceci:

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

J'espère que ça aide.

Une chose à prendre en compte lors de l’utilisation du cache distribué est que QueryCache est local et que son éviction sur un noeud ne l’expulse pas des autres. Un autre problème est le suivant: l'expulsion d'une région d'entité sans expulser la région de requête entraînera une sélection de N + 1 lors de la tentative de récupération de la date à partir du cache de la requête. Bonnes lectures sur ce sujet, ici .

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top