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

22,528

Solution 1

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

Solution 2

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

Solution 3

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

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

Solution 4

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.

Solution 5

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.

Share:
22,528
Dougnukem
Author by

Dougnukem

Worked at some game companies and social game startups, working on a new web startup currently. Personal Website

Updated on July 17, 2022

Comments

  • Dougnukem
    Dougnukem almost 2 years

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