データベースのヒットを回避するためにHibernateの第2レベルのキャッシュを使用するアプリケーションがあります。
MySQL管理者などの外部プロセスがデータベースの変更(更新/挿入/削除)に直接接続しているときに、JavaアプリケーションのHibernate2次キャッシュを無効にする簡単な方法があるかどうか疑問に思いました。
第2レベルのキャッシュ実装として EHCache を使用しています。
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)と@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)を組み合わせて使用し、各エンティティのタイムスタンプを使用してオプティミスティック同時実行制御を有効にしません。
SessionFactoryには、第2レベルのキャッシュを管理するためのメソッドが含まれています。- キャッシュの管理
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の第2レベルのキャッシュが、エンティティを照会しない限り、DBでエンティティが変更されたことを知る実際の方法はありません(これは、キャッシュがユーザーを保護しているものです)。したがって、解決策として、何らかのメソッドを呼び出して、第2レベルのキャッシュにすべてを強制的に削除させることができます(ロックと同時実行制御がないため、進行中のトランザクションが古いデータを「読み取る」または更新するリスクがあります)。
ChssPly76's コメントに基づいて、これは2次キャッシュからすべてのエンティティを削除するメソッドです(このメソッドは、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);
}
}
SessionFactory その目的のために正確にたくさんのevict()
メソッドがあります:
sessionFactory.evict(MyEntity.class); // remove all MyEntity instances
sessionFactory.evict(MyEntity.class, new Long(1)); // remove a particular MyEntity instances
HibernateとJPAの両方が、基盤となる第2レベルのキャッシュへの直接アクセスを提供するようになりました。
sessionFactory.getCache().evict(..);
entityManager.getCache().evict(..)
すべてのHibernateキャッシュを無効にする方法を探していたところ、次の便利なスニペットが見つかりました。
sessionFactory.getCache().evictQueryRegions();
sessionFactory.getCache().evictDefaultQueryRegion();
sessionFactory.getCache().evictCollectionRegions();
sessionFactory.getCache().evictEntityRegions();
それが他の誰かに役立つことを願っています。
あなたはこれをやってみることができます:
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;
}
お役に立てば幸いです。
分散キャッシュを使用する際に考慮すべきことの1つは、QueryCacheがローカルであり、1つのノードで削除しても、他のノードから削除されないことです。もう1つの問題は、クエリ領域を削除せずにエンティティ領域を削除すると、クエリキャッシュから日付を取得しようとしたときに、N +1個の選択が発生することです。このトピックに関する良い読み物 ここ 。