Hibernate Caching Tutorial with Examples


Caching in Hibernate

One of the major benefit of using Hibernate in large application is it’s support for caching, hence reducing database queries and better performance.

Caching is all about application performance optimization and it sits between your application and the database to avoid the number of database hits as many as possible to give a better performance for performance critical applications.

Caching is important to Hibernate as well which utilizes a multilevel caching schemes as explained below:
Hibernate Caching Tutorial with Examples

First-level cache:

The first-level cache is the Session cache and is a mandatory cache through which all requests must pass. The Session object keeps an object under its own power before committing it to the database.

If you issue multiple updates to an object, Hibernate tries to delay doing the update as long as possible to reduce the number of update SQL statements issued. If you close the session, all the objects being cached are lost and either persisted or updated in the database.

Query-level cache:

Hibernate also implements a cache for query resultsets that integrates closely with the second-level cache.

This is an optional feature and requires two additional physical cache regions that hold the cached query results and the timestamps when a table was last updated. This is only useful for queries that are run frequently with the same parameters.

Second-level cache:

Second level cache is an optional cache and first-level cache will always be consulted before any attempt is made to locate an object in the second-level cache. The second-level cache can be configured on a per-class and per-collection basis and mainly responsible for caching objects across sessions.

Any third-party cache can be used with Hibernate. An org.hibernate.cache.CacheProvider interface is provided, which must be implemented to provide Hibernate with a handle to the cache implementation.

Hibernate uses first-level cache by default and you have nothing to do to use first-level cache. Let's go straight to the optional second-level cache. Not all classes benefit from caching, so it's important to be able to disable the second-level cache.

The Hibernate second-level cache is set up in two steps. First, you have to decide which concurrency strategy to use. After that, you configure cache expiration and physical cache attributes using the cache provider.

Concurrency strategies:

A concurrency strategy is a mediator which responsible for storing items of data in the cache and retrieving them from the cache. If you are going to enable a second-level cache, you will have to decide, for each persistent class and collection, which cache concurrency strategy to use.
  • Transactional: Use this strategy for read-mostly data where it is critical to prevent stale data in concurrent transactions,in the rare case of an update.
  • Read-write: Again use this strategy for read-mostly data where it is critical to prevent stale data in concurrent transactions,in the rare case of an update.
  • Nonstrict-read-write: This strategy makes no guarantee of consistency between the cache and the database. Use this strategy if data hardly ever changes and a small likelihood of stale data is not of critical concern.
  • Read-only: A concurrency strategy suitable for data which never changes. Use it for reference data only.

Hibernate Configuration for Second Level EHCache

Second level cache is disabled by default in hibernate, so we would need to enable it and add some configurations to get it working. Our hibernate.cfg.xml file looks like below.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
  "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
  "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
    <session-factory>
        <property name="hibernate.connection.driver_class">net.sourceforge.jtds.jdbc.Driver</property>
        <property name="hibernate.connection.password">SBMDB@08$2012</property>
        <property name="hibernate.connection.url">jdbc:jtds:sqlserver://172.16.5.23:1433/SBMUATDB</property>
        <property name="hibernate.connection.username">SBM_QA</property>
        <property name="hibernate.dialect">org.hibernate.dialect.SQLServerDialect</property>
     <property name="connection.pool_size">1</property>
        <property name="hbm2ddl.auto">create</property>
        <property name="show_sql">true</property>  
  <property name="format_sql">true</property>  
  
  <property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</property>                  
   <!-- For singleton factory -->        
   <!-- <property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory</property>          -->      
  <!-- enable second level cache and query cache -->         
  <property name="hibernate.cache.use_second_level_cache">true</property>  
  <property name="hibernate.cache.use_query_cache">true</property>          
  <property name="net.sf.ehcache.configurationResourceName">/myehcache.xml</property>   
  
        <mapping class="com.tutorialsdesk.hibernate.bean.Student"/>
        <mapping class="com.tutorialsdesk.hibernate.bean.StudentMarksDetails"/>
       <!--  <mapping class="com.tutorialsdesk.hibernate.bean.StudentAddress"/>  -->
    </session-factory>
</hibernate-configuration>


Some important points about hibernate configurations are:
  1. hibernate.cache.region.factory_class is used to define the Factory class for Second level caching, I am using org.hibernate.cache.ehcache.EhCacheRegionFactory for this. If you want the factory class to be singleton, you should use org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory class. If you are using Hibernate 3, corresponding classes will be net.sf.ehcache.hibernate.EhCacheRegionFactory and net.sf.ehcache.hibernate.SingletonEhCacheRegionFactory.
  2. hibernate.cache.use_second_level_cache is used to enable the second level cache.
  3. hibernate.cache.use_query_cache is used to enable the query cache, without it HQL queries results will not be cached.
  4. net.sf.ehcache.configurationResourceName is used to define the EHCache configuration file location, it’s an optional parameter and if it’s not present EHCache will try to locate ehcache.xml file in the application classpath.

EHCache Configuration File

Our EHCache configuration file looks like below.
<?xml version="1.0" encoding="UTF-8"?> 
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    xsi:noNamespaceSchemaLocation="ehcache.xsd" updateCheck="true"    monitoring="autodetect" dynamicConfig="true">       
<diskStore path="java.io.tmpdir/ehcache" />      
 <defaultCache maxEntriesLocalHeap="10000" eternal="false"        timeToIdleSeconds="120" timeToLiveSeconds="120" diskSpoolBufferSizeMB="30"        maxEntriesLocalDisk="10000000" diskExpiryThreadIntervalSeconds="120"        memoryStoreEvictionPolicy="LRU" statistics="true">         
 <persistence strategy="localTempSwap" />     
 </defaultCache>       
 <cache name="employee" maxEntriesLocalHeap="10000" eternal="false"        timeToIdleSeconds="5" timeToLiveSeconds="10">         
 <persistence strategy="localTempSwap" />    
  </cache>       
  <cache name="org.hibernate.cache.internal.StandardQueryCache"        maxEntriesLocalHeap="5" eternal="false" timeToLiveSeconds="120">         
  <persistence strategy="localTempSwap" />    
   </cache>       
   <cache name="org.hibernate.cache.spi.UpdateTimestampsCache"        maxEntriesLocalHeap="5000" eternal="true">        
    <persistence strategy="localTempSwap" />     
    </cache> 
    </ehcache> 


That's it, now we have second-level caching enabled for the Employee class and Hibernate now hits the second-level cache whenever you navigate to a Employee or when you load a Employee by identifier.

You should analyze your all the classes and choose appropriate caching strategy for each of the classes. Sometime, second-level caching may downgrade the performance of the application. So it is recommended to benchmark your application first without enabling caching and later on enable your well suited caching and check the performance. If caching is not improving system performance then there is no point in enabling any type of caching.

Enable Hibernate Second Level Cache at Table Level by Annotation

In Hibernate Annotation, we use @Cache to set table level cache. We also need to set @Cacheable. The class CacheConcurrencyStrategy of the package org.hibernate.annotations, provides the caching.
@Entity
@Table(name="college")
@Cacheable
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class Employee implements Serializable {


// Implementions 

}

Enable Hibernate Second Level Cache at Column Level by Annotation

In Hibernate Collections to cache the collection at column level @Cache is used and CacheConcurrencyStrategy class provides the caching. We need to set it at property level in our entity.
   @ElementCollection
 @CollectionTable(name="student", joinColumns=@JoinColumn(name="college_id"))
 @Column(name="student_name")
 @Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
 private Set<String> students;

Enable Hibernate Query-level Cache:

To use the query cache, you must first activate it using the hibernate.cache.use_query_cache="true" property in the configuration file. By setting this property to true, you make Hibernate create the necessary caches in memory to hold the query and identifier sets.

Next, to use the query cache, you use the setCacheable(Boolean) method of the Query class. For example:
Session session = SessionFactory.openSession();
Query query = session.createQuery("FROM EMPLOYEE");
query.setCacheable(true);
List users = query.list();
SessionFactory.closeSession();

Hibernate also supports very fine-grained cache support through the concept of a cache region. A cache region is part of the cache that's given a name.
Session session = SessionFactory.openSession();
Query query = session.createQuery("FROM EMPLOYEE");
query.setCacheable(true);
query.setCacheRegion("employee");
List users = query.list();
SessionFactory.closeSession();

This code uses the method to tell Hibernate to store and look for the query in the employee area of the cache.

Hope we are able to explain you Caching in Hibernate, if you have any questions or suggestions please write to us using contact us form.(Second Menu from top left).

Please share us on social media if you like the tutorial.