2009-10-21 9 views
22

Nous avons une application qui utilise la mise en mémoire cache de second niveau de Hibernate pour éviter les accès à la base de données. Je me demandais s'il existe un moyen simple d'invalider le cache Hibernate 2nd niveau de l'application Java lorsqu'un processus externe tel qu'un administrateur MySQL est directement connecté pour modifier la base de données (update/insert/delete).Hibernate invalidation de cache de second niveau lorsqu'un autre processus modifie la base de données

Nous utilisons EHCache comme implémentation de cache de second niveau. Nous utilisons un mélange de @Cache (utilisation = CacheConcurrencyStrategy.READ_WRITE) et @Cache (usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE), et nous n'avons pas de contrôle de concurrence optimiste activé en utilisant des horodatages sur chaque entité.

Le SessionFactory contient des méthodes pour gérer le 2ème niveau du cache: - Managing the 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 parce que nous annoter classes d'entités individuelles avec @Cache, il n'y a pas de place centrale pour nous « fiable » (par exemple, pas de manuel étapes) ajouter cela à 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); 
    } 
} 

Il n'y a pas de véritable moyen pour le cache de niveau 2 de mise en veille prolongée de savoir qu'une entité a changé dans la DB, à moins qu'il interroge cette entité (qui est ce que le cache vous protège de). Donc peut-être comme une solution nous pourrions simplement appeler une méthode pour forcer le cache de second niveau à tout expulser (encore une fois en raison du manque de verrouillage et de contrôle de concurrence, vous risqueriez de "lire" ou de mettre à jour les données périmées).

Répondre

14

Sur la base ChssPly76's commentaires est ici une méthode qui évince toutes les entités du 2 cache de niveau (nous pouvons exposer cette méthode pour les admins par JMX ou tout autre administrateur outils):

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

Je souhaite effacer les données de cache du cache de second niveau en appelant la méthode ci-dessous: - sessionFactory.getCache(). EvictEntityRegions(); Je veux juste savoir, y a-t-il un mal à faire cela? Par exemple: - Que se passe-t-il si j'essaie d'effacer le cache au milieu de la transaction? –

11

SessionFactory a beaucoup de evict() méthodes 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 
+0

Existe-t-il un moyen fiable d'expulser toutes les instances de toutes les classes annotées avec @Cache à partir du cache de second niveau. Nous annotons généralement les classes pour activer le cache de second niveau et il n'y a donc pas vraiment d'emplacement central pour mettre à jour le code lorsqu'une nouvelle entité est ajoutée au cache de second niveau. – Dougnukem

+1

Pour être honnête, l'éviction de toutes les instances ** semble être plutôt extrême. Faites-le assez souvent et vous pourriez vous retrouver avec une performance pire que sans cache du tout. Cela dit, vous pouvez obtenir les noms de toutes les entités via la méthode 'getAllClassMetadata()' (les noms sont des clés de la carte retournée) et appeler 'evictEntity()' pour chacun. – ChssPly76

+0

Ceci est seulement une occasion extrêmement rare quand nous devons faire une production "HOTFIX" de la base de données sans enlever les serveurs et nos outils d'administration de service client existants ne peuvent pas le faire (et nous ne voulons pas exposer une commande d'administration vous permet d'exécuter un SQL arbitraire). Ensuite, en permettant à un administrateur externe d'exécuter SQL, nous perdons tous les audits et enregistrements de transactions. – Dougnukem

8

les deux mise en veille prolongée et JPA offrent désormais un accès direct au cache 2ème niveau sous-jacent:

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

Je cherchais comment annuler toutes les caches Hibernate et je trouve cet extrait utile:

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

Hope it helps à quelqu'un d'autre.

1

Vous pouvez essayer de faire 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.

Questions connexes