Guide to Hibernate Second Level Cache

Caching is the facility provided by ORM frameworks that helps the users to get fast-running web applications while helping the framework itself to reduce the number of queries made to the database in a single transaction.

1. Caching in Hibernate

Hibernate also provides caching functionality in two layers.

  • First-level Cache: This is enabled by default and works in Session scope. It has been explained in detail in this article.
  • Second-level Cache: This is separate from the first-level cache and is available to be used globally in SessionFactory scope.

The above definitions can be understood as that:

  • the entities stored in the second level cache will be available to all the sessions created using that particular session factory.
  • once the SessionFactory is closed, all cache associated with it die and the cache manager also closes down.
  • if we have two instances of SessionFactory (highly discouraged), we will have two cache managers in our application and while accessing cache stored in a physical store, we might get unpredictable results like cache-miss.
hibernate first and second level cache_example
Hibernate first and second level cache

In this tutorial, I am giving concepts around hibernate second-level cache and giving examples using code snippets.

2. How does Second Level Cache Work in Hibernate?

Let us write all the facts point by point to better understand the internal working related to second-level caches.

  1. Whenever hibernate session tries to load an entity, the very first place it looks for a cached copy of the entity in first-level cache (associated with a particular hibernate Session).
  2. If a cached copy of the entity is present in first-level cache, it is returned as the result of load() method.
  3. If there is no cached entity in the first-level cache, then the second-level cache is looked up for the cached entity.
  4. If second-level cache has the cached entity, it is returned as the result of load() method. But, before returning the entity, it is stored in first level cache also so that the next invocation to load() method for that entity will return the entity from the first level cache itself, and there will not be need to go to the second level cache again.
  5. If the entity is not found in first level cache and second level cache also, then a database query is executed and the entity is stored in both cache levels, before returning as the response to load() method.
  6. Second-level cache validates itself for modified entities if the modification has been done through hibernate session APIs.
  7. If some user or process makes changes directly in the database, there is no way that the second-level cache update itself until “timeToLiveSeconds” duration has passed for that cache region. In this case, it is a good idea to invalidate the whole cache and let hibernate build its cache once again. You can use sessionFactory.evictEntity() in a loop to invalidate the whole Hibernate second-level cache.
/**
 * Evicts all second level cache hibernate entites. This is generally only
 * needed when an external application modifies the 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);
    }
}

3. Demo

To understand more using examples, I wrote an application for testing in which I configured EhCache as 2nd level cache. Lets see various scenarios:

3.1. Entity is Fetched for the First Time

DepartmentEntity department = (DepartmentEntity) session.load(DepartmentEntity.class, new Integer(1));
System.out.println(department.getName());

System.out.println(HibernateUtil.getSessionFactory().getStatistics().getEntityFetchCount());           //Prints 1
System.out.println(HibernateUtil.getSessionFactory().getStatistics().getSecondLevelCacheHitCount());   //Prints 0

Explanation: Entity is not present in either 1st or 2nd level cache so, it is fetched from the database.

3.2. Entity is Requested Again in Same Session

//Entity is fecthed very first time
DepartmentEntity department = (DepartmentEntity) session.load(DepartmentEntity.class, new Integer(1));
System.out.println(department.getName());

//fetch the department entity again
department = (DepartmentEntity) session.load(DepartmentEntity.class, new Integer(1));
System.out.println(department.getName());

System.out.println(HibernateUtil.getSessionFactory().getStatistics().getEntityFetchCount());           //Prints 1
System.out.println(HibernateUtil.getSessionFactory().getStatistics().getSecondLevelCacheHitCount());   //Prints 0

Explanation: Entity is present in the first-level cache so, it is fetched from the second-level cache. No need to go to the second-level cache.

3.3. Entity is Evicted from First-level Cache and Requested Again

//Entity is fecthed very first time
DepartmentEntity department = (DepartmentEntity) session.load(DepartmentEntity.class, new Integer(1));
System.out.println(department.getName());

//fetch the department entity again
department = (DepartmentEntity) session.load(DepartmentEntity.class, new Integer(1));
System.out.println(department.getName());

//Evict from first level cache
session.evict(department);

department = (DepartmentEntity) session.load(DepartmentEntity.class, new Integer(1));
System.out.println(department.getName());

System.out.println(HibernateUtil.getSessionFactory().getStatistics().getEntityFetchCount());           //Prints 1
System.out.println(HibernateUtil.getSessionFactory().getStatistics().getSecondLevelCacheHitCount());   //Prints 1

Explanation: First time entity is fetched from the database that causes it to store in 1st and 2nd level cache. Second load() call fetched from first-level cache. Then we evicted the entity from 1st level cache.

So third load() call goes to second level cache and getSecondLevelCacheHitCount() returns 1.

3.4. Entity is Requested Again in Another Session

//Entity is fecthed very first time
DepartmentEntity department = (DepartmentEntity) session.load(DepartmentEntity.class, new Integer(1));
System.out.println(department.getName());

//fetch the department entity again
department = (DepartmentEntity) session.load(DepartmentEntity.class, new Integer(1));
System.out.println(department.getName());

//Evict from first level cache
session.evict(department);

department = (DepartmentEntity) session.load(DepartmentEntity.class, new Integer(1));
System.out.println(department.getName());

department = (DepartmentEntity) anotherSession.load(DepartmentEntity.class, new Integer(1));
System.out.println(department.getName());

System.out.println(HibernateUtil.getSessionFactory().getStatistics().getEntityFetchCount());           //Prints 1
System.out.println(HibernateUtil.getSessionFactory().getStatistics().getSecondLevelCacheHitCount());   //Prints 2

Explanation: When another session created from the same session factory tries to get the entity, it is successfully looked up in the second-level cache and no database call is made.

So now we are clear on how second-level cache is used by hibernate.

Happy Learning !!

Leave a Reply

73 Comments
Most Voted
Newest Oldest
Inline Feedbacks
View all comments

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions, and frequently asked interview questions.