Hibernate Caches — second-level cache

  • Fist level cache: This is enabled by default and works in session scope.
  • Second level cache: This is apart from first level cache which is available to be used globally in session factory scope.

Above statement means, second level cache is created in session factory scope and is available to be used in all sessions which are created using that particular session factory.
It also means that once session factory is closed, all cache associated with it die and cache manager also closed down.
Further, It also means that if you have two instances of session factory (normally no application does that), you will have two cache managers in your application and while accessing cache stored in physical store, you might get unpredictable results like cache-miss.

hibernate first and second level cache_example

hibernate first and second level cache_example

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

How second level cache works

Lets write all the facts point by point:

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

The key characteristic of the second-level cache is that is is used across sessions, which also differentiates it from the session cache, which only – as the name says – has session scope. Hibernate provides a flexible concept to exchange cache providers for the second-level cache. By default Ehcache is used as caching provider. However more sophisticated caching implementation can be used like the distributed JBoss Cache or Oracle Coherence.

First we have to modify our code sample so that we now load the Person object in two sessions. The source code then looks as follows

public void loadInTwoSessions (){ 
 // loading in first session 
 Session session = getSessionFactory().openSession(); 
 Transaction tx = session.beginTransaction(); 
 Person p = (Person) session.load(Person.class, 1L); 
 System.out.println(p.getFirstName()); tx.commit(); 
 session.close(); 
 // loading in second session 
 session = getSessionFactory().openSession(); 
 tx = session.beginTransaction(); 
 p = (Person) session.load(Person.class, 1L);    
 System.out.println(p.getFirstName());        
 tx.commit(); 
 session.close();        
}

As we have not activated the second level cache, we expect the SQL queries to be executed twice. Looking at the PurePath of this transactions verifies our asumption.

Loading a person object in two sessions without second-level cache

Loading a person object in two sessions without second-level cache

Now we activate the second-level cache. Activating the second level cache requires us change to Hibernate configuration file and enable second-level caching by adding and additionally specify the cache provider as shown below.

 <property name="hibernate.cache.use_second_level_cache">true</property> 
<property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property> 

In this example I am using Ehcache for demonstration purposes. In order to enable caching of our Person objects if have to specify the caching configuration in theehcache.xml file.  The actual cache configuration depends on the caching provider. For Ehcache the configuartion is defined as follows. The configuration for the Person class used in the example is boiler-plate Ehcache configuration. It can be adopted to specific needs. Describing all possible configurations options like using mulitple cache regions etc. is beyond scope of this post.

<cache 
  name="com.dynatrace.samples.database.Person"    
  maxElementsInMemory="300"    
  eternal="true"    
  overflowToDisk="false"  
  timeToIdleSeconds="12000"  
  timeToLiveSeconds="12000"  
  diskPersistent="false"  
  diskExpiryThreadIntervalSeconds="120"  
  memoryStoreEvictionPolicy="LRU"        />

Finally we have to configure caching also at Hibernate level. Hibernate supports mulitple settings for caching. As we are only reading data it the moment a read-only cache is sufficient for our purposes. Hibernate for sure supports  read-write cache as well and also transactional caches in case this is supported by the cache provider.  The following liine in the hibernate configuration enable read-only caching for Person objects. Alternatively also Hibernate associations could be used.

<cache usage=”read-only” />

Now we expect the object to be retrieved from the second-level the second time it is loaded. A PurePath trace verifies this assumption.  Now, only the first time a database call gets executed.

Loading in two sessions with enabled second-level cache

Loading a person object in two sessions with enabled second-level cache

Conclusion

The second level cache is powerful mechanism for improving performance and scalability of your database driven application. Read-only caches are easy to handle, while read-write caches are more subtile in their behavior. Especially the interaction with the Hibernate session can lead to unwanted behavior. Sessions should therefore be used as what they are designed for – a transactional context. There are more details on the second-level cache I did not elaborate on like synchronization or replication behavior. However the combination of the three caching articles should provide good insight into Hibernate caching behavior.

From HERE and THERE

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s