当另一个进程修改数据库时,Hibernate 二级缓存失效

发布于 2024-08-08 01:23:33 字数 1497 浏览 11 评论 0原文

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

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

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).

We are using EHCache as our 2nd level cache implementation.

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.

The SessionFactory contains methods to manage the 2nd level 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

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.

// 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).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

懵少女 2024-08-15 01:23:34

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

Based on ChssPly76's comments 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):

/**
 * 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);
    }
}
反差帅 2024-08-15 01:23:34

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

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

SessionFactory has plenty of evict() methods precisely for that purpose:

sessionFactory.evict(MyEntity.class); // remove all MyEntity instances
sessionFactory.evict(MyEntity.class, new Long(1)); // remove a particular MyEntity instances
意中人 2024-08-15 01:23:34

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

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

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

sessionFactory.getCache().evict(..);
entityManager.getCache().evict(..)
暖阳 2024-08-15 01:23:34

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

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

希望它对其他人有帮助。

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

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

Hope it helps to someone else.

南城旧梦 2024-08-15 01:23:34

您可以尝试这样做:

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

我希望它有帮助。

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.

剩余の解释 2024-08-15 01:23:34

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

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文