database 当另一个进程修改数据库时休眠二级缓存失效

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1603846/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-08 07:29:54  来源:igfitidea点击:

Hibernate 2nd level cache invalidation when another process modifies the database

databasehibernatetransactionsehcachesecond-level-cache

提问by Dougnukem

We have an application that uses Hibernate's 2nd level caching to avoid database hits.

我们有一个应用程序,它使用 Hibernate 的二级缓存来避免数据库命中。

I was wondering if there is some easy way to invalidate the Java application's Hibernate 2nd level cache when an outside process such as a MySQL administrator directly connected to modify the database (update/insert/delete).

我想知道当外部进程(例如 MySQL 管理员)直接连接以修改数据库(更新/插入/删除)时,是否有一些简单的方法可以使 Java 应用程序的 Hibernate 二级缓存无效。

We are using EHCacheas our 2nd level cache implementation.

我们使用EHCache作为我们的二级缓存实现。

We use a mix of @Cache(usage = CacheConcurrencyStrategy.READ_WRITE) and @Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE), and we don't have Optimistic concurrency control enabled using timestamps on each entity.

我们混合使用@Cache(usage = CacheConcurrencyStrategy.READ_WRITE) 和@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE),并且我们没有在每个实体上使用时间戳启用乐观并发控制。

The SessionFactory contains methods to manage the 2nd level cache: - Managing the Caches

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

But because we annotate individual entity classes with @Cache, there's no central place for us to "reliably" (e.g. no manual steps) add that to the list.

但是因为我们使用@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);
   }
}

There's no real way for Hibernate's 2nd level cache to know that an entity changed in the DB unless it queries that entity (which is what the cache is protecting you from). So maybe as a solution we could simply call some method to force the second level cache to evict everything (again because of lack of locking and concurrency control you risk in progress transactions from "reading" or updating stale data).

Hibernate 的二级缓存没有真正的方法来知道数据库中的实体发生了变化,除非它查询该实体(这是缓存保护您免受的伤害)。因此,作为一种解决方案,我们可以简单地调用一些方法来强制二级缓存驱逐所有内容(同样由于缺乏锁定和并发控制,您可能会因“读取”或更新陈旧数据而面临正在进行的事务的风险)。

采纳答案by Dougnukem

Based on ChssPly76'scomments here's a method that evicts all entities from 2nd level cache (we can expose this method to admins through JMX or other admin tools):

基于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);
    }
}

回答by ChssPly76

SessionFactoryhas plenty of evict()methods precisely for that purpose:

SessionFactory有很多evict()方法正是为了这个目的:

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

回答by Bozho

Both hibernate and JPA now provide direct access to the underlying 2nd level cache:

Hibernate 和 JPA 现在都提供对底层二级缓存的直接访问:

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

回答by jelies

I was searching how to invalidate all Hibernate caches and I found this useful snippet:

我正在搜索如何使所有 Hibernate 缓存无效,我发现了这个有用的片段:

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

Hope it helps to someone else.

希望对其他人有所帮助。

回答by Aalkhodiry

You may try doing this:

您可以尝试这样做:

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

I hope It helps.

我希望它有帮助。

回答by Oleg Kuts

One thing to take into account when using distributed cache is that QueryCache is local, and evicting it on one node, does not evicts it from other. Another issue is - evicting Entity region without evicting Query region will cause N+1 selects,when trying to retrieve date from Query cache. Good readings on this topic here.

使用分布式缓存时要考虑的一件事是 QueryCache 是本地的,在一个节点上驱逐它,不会从其他节点驱逐它。另一个问题是 - 在尝试从查询缓存中检索日期时,在不驱逐查询区域的情况下驱逐实体区域将导致 N+1 个选择。这里有关于这个主题的好读物。