28

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

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

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

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

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 的二级缓存没有真正的方法知道数据库中的实体已更改,除非它查询该实体(这是缓存保护您免受的)。因此,也许作为一种解决方案,我们可以简单地调用一些方法来强制二级缓存驱逐所有内容(同样由于缺乏锁定和并发控制,您可能会在进行中的事务中“读取”或更新陈旧数据)。

4

6 回答 6

16

根据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);
    }
}
于 2009-10-21T23:43:52.680 回答
12

SessionFactory有很多evict()方法正是为此目的:

sessionFactory.evict(MyEntity.class); // remove all MyEntity instances
sessionFactory.evict(MyEntity.class, new Long(1)); // remove a particular MyEntity instances
于 2009-10-21T22:00:23.447 回答
8

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

sessionFactory.getCache().evict(..);
entityManager.getCache().evict(..)
于 2012-05-15T09:01:23.487 回答
4

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

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

希望对其他人有所帮助。

于 2014-03-11T10:03:08.697 回答
1

您可以尝试这样做:

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

我希望它有帮助。

于 2014-05-28T08:57:00.203 回答
0

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

于 2019-07-03T17:37:39.323 回答