Hibernate Cache

Hibernate comes with three different caching mechanisms – first level (session cache), second level and query cache. How the Hibernate caches work and interact with each other is important when you need to increase performance – just enabling caching in your entity with an Annotation (or in classic .hbm.xml mapping file) is easy. But understanding what and how things happens behind the scenes is not. You might even end up with a less performing system if you do not know what you are doing.

The 1st level cache

It is also known as session cache. The first level cache is where Hibernate keeps track of the possible dirty states of the ongoing Session’s loaded and touched entities. The ongoing Session represents a unit of work and is always used and can not be turned of. This cache is enabled by default.

Let us have a look at the following code sample. We create two queries to load a person object from cache. As we are loading the same object twice, we expect it to be retrieved from the cache.

Query query = session.createQuery("from Employee emp where emp.firstname='Puneet'");
Iterator it = query.list().iterator();
while (it.hasNext ()){
    Employee emp = (Employee) it.next();
query = session.createQuery("from Employee emp where emp.firstname='Puneet'");
it = query.list().iterator();
while (it.hasNext ()){
    Employee emp = (Employee) it.next();

When I run the above code, I was expecting the query to be executed only once. But when I see the logs, two queries have been executed as shown in Figure 16.1

Figure 16.1 Figure 16.1

To enable first level cache, use load() method and pass the key directly as shown below. As the data is always by default lazy loading in Hibernate, we will use System.out.println() to force the Hibernate to load data.

Now when I run the program, only one database query is issued as shown in Figure 16.2. The same behaviour could have been achieve by using get instead of load.

Employee employee1 = (Employee) session.load(Employee.class, 1L);
Employee employee2 = (Employee) session.load(Employee.class, 1L);   

Figure 16.2 Figure 16.2

The Query Cache

The query cache effectively holds on to the identifiers for an individual query.

The Query cache does not cache the state of the actual entities in the result set; it caches only identifier values and results of value type. So the query cache should always be used in conjunction with the second-level cache.

Configuration of the query cache is described below:

You will first need to enable the query cache:

<property name="hibernate.cache.use_query_cache">true</property>

This setting creates two new cache regions: one holding cached query result sets (org.hibernate.cache.StandardQueryCache), the other holding timestamps of the most recent updates to queryable tables (org.hibernate.cache.UpdateTimestampsCache). Note that the query cache does not cache the state of the actual entities in the result set; it caches only identifier values and results of value type. The query cache should always be used in conjunction with the second-level cache.

<property name="hibernate.cache.use_second_level_cache">true</property> 
<property name="hibernate.cache.region.factory_class">net.sf.ehcache.hibernate.EhCacheRegionFactory</property>

Once enabled via the configuration of Hibernate, it is simply a matter of calling setCacheable(true) on your Query or Criteria object.

<property name="hibernate.cache.use_second_level_cache">true</property> 
<property name="hibernate.cache.region.factory_class">net.sf.ehcache.hibernate.EhCacheRegionFactory</property>

The Second Level Cache

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.

You have the option to tell Hibernate which caching implementation to use by specifying the name of a class that implements org.hibernate.cache.CacheProvider using the property hibernate.cache.provider_class. Hibernate is bundled with a number of built-in integrations with the open-source cache providers that are listed below. Note that versions prior to 3.2 use EhCache as the default cache provider.

Table 1.1 Cache Providers

Cache Provider class Type Cluster Safe Query Cache Supported
Hashtable (not intended for production use) org.hibernate.cache.HashtableCacheProvider. memory   yes
EHCache org.hibernate.cache.EhCacheProvider memory, disk   yes
OSCache org.hibernate.cache.OSCacheProvider memory, disk   yes
SwarmCache org.hibernate.cache.SwarmCacheProvider clustered (ip multicast) yes (clustered invalidation)  
JBoss Cache 1.x org.hibernate.cache.TreeCacheProvider clustered (ip multicast), transactional yes (replication) yes (clock sync req.)
JBoss Cache 2 org.hibernate.cache.jbc2.JBossCacheRegionFactory clustered (ip multicast), transactional yes (replication or invalidation) yes (clock sync req.)

The <cache> element of a class or collection mapping has the following form:


1. usage (required) specifies the caching strategy: transactional, read-write, nonstrict-read-write orread-only
2. region (optional: defaults to the class or collection role name): specifies the name of the second level cache region
3. include (optional: defaults to all) non-lazy: specifies that properties of the entity mapped withlazy=”true” cannot be cached when attribute-level lazy fetching is enabled

Alternatively, you can specify <class-cache> and <collection-cache> elements in hibernate.cfg.xml.

The usage attribute specifies a cache concurrency strategy.

1. Strategy: read only

If your application needs to read, but not modify, instances of a persistent class, a read-only cache can be used. This is the simplest and optimal performing strategy. It is even safe for use in a cluster.

<class name="eg.Immutable" mutable="false">
    <cache usage="read-only"/>

2. Strategy: read/write

If the application needs to update data, a read-write cache might be appropriate. This cache strategy should never be used if serializable transaction isolation level is required. If the cache is used in a JTA environment, you must specify the property hibernate.transaction.manager_lookup_class and naming a strategy for obtaining the JTA TransactionManager. In other environments, you should ensure that the transaction is completed when Session.close() or Session.disconnect() is called. If you want to use this strategy in a cluster, you should ensure that the underlying cache implementation supports locking. The built-in cache providers do not support locking.

<class name="eg.Cat" .... >
    <cache usage="read-write"/>
    <set name="kittens" ... >
        <cache usage="read-write"/>

3. Strategy: nonstrict read/write

If the application only occasionally needs to update data (i.e. if it is extremely unlikely that two transactions would try to update the same item simultaneously), and strict transaction isolation is not required, a nonstrict-read-write cache might be appropriate. If the cache is used in a JTA environment, you must specify hibernate.transaction.manager_lookup_class. In other environments, you should ensure that the transaction is completed when Session.close() or Session.disconnect() is called.

4. Strategy: transactional

The transactional cache strategy provides support for fully transactional cache providers such as JBoss TreeCache. Such a cache can only be used in a JTA environment and you must specifyhibernate.transaction.manager_lookup_class.

Now to activate 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.region.factory_class">net.sf.ehcache.hibernate.EhCacheRegionFactory</property>

In order to enable caching of our Employee objects if have to specify the caching configuration in the ehcache.xml file. The actual cache configuration depends on the caching provider. For Ehcache the configuartion is defined as follows.

Note : Include the ehcache.xml in classpath.

<?xml version="1.0" encoding="UTF-8"?>
    <diskStore path="java.io.tmpdir"/>
    <cache name="com.kruders.bean.Employee"
        maxElementsInMemory="300" eternal="true" overflowToDisk="false" />
    <cache name="org.hibernate.cache.StandardQueryCache"
        maxElementsInMemory="300" eternal="true" overflowToDisk="false" />
You can download the source code of this example here.

No comments yet.

Leave a Reply