Hibernate EhCache configuration tutorial

Caching is facility provided by ORM frameworks which help users to get fast running web application, while help framework itself to reduce number of queries made to database in a single transaction. Hibernate also provide this caching functionality, in two layers.

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

In this tutorial, I am giving an example using ehcache configuration as second level cache in hibernate.

Hibernate with EhCache

Hibernate with EhCache

Sections in this post:
How second level cache works
About EhCache
Configuring EhCache
Configuring entity objects
Query caching
Example application
Sourcecode download

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.

About EhCache

Terracotta Ehcache is a popular open source Java cache that can be used as a Hibernate second level cache. It can be used as a standalone second level cache, or can be configured for clustering to provide a replicated coherent second level cache.

Hibernate ships with the ehcache library. If you want any particular version of ehcache, visit the Terracotta Ehcache download site:

http://www.terracotta.org/products/enterprise-ehcache

The maven dependency is for Ehcache 2.0 and any upgrades is:

<dependency>
	<groupId>net.sf.ehcache</groupId>
	<artifactId>ehcache</artifactId>
	<version>[2.0.0]</version>
	<type>pom</type>
</dependency>

Configuring EhCache

To configure ehcache, you need to do two steps:

  1. configure Hibernate for second level caching
  2. specify the second level cache provider

Hibernate 3.3 and above

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

Hibernate 3.2 and below

<property key="hibernate.cache.use_second_level_cache">true</property>
<property name="hibernate.cache.region.provider_class">net.sf.ehcache.hibernate.EhCacheProvider</property>

Configuring entity objects

This may done in two ways.

1) If you are using hbm.xml files then use below configuration:

<class name="com.application.entity.DepartmentEntity" table="...">
    <cache usage="read-write"/>
</class>

2) Otherwise, if you are using annotations, use these annotations:

@Entity
@Cache(usage=CacheConcurrencyStrategy.READ_ONLY, 
region="department")
public class DepartmentEntity implements Serializable 
{
	//code
}

For both options, caching strategy can be of following types:

  • none : No caching will happen.
  • read-only : If your application needs to read, but not modify, instances of a persistent class, a read-only cache can be used.
  • read-write : If the application needs to update data, a read-write cache might be appropriate.
  • 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.
  • 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 specify hibernate.transaction.manager_lookup_class.

Query caching

You can also enable query caching. To do so configure it in your hbm.xml:

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

and where queries are defined in your code, add the method call setCacheable(true) to the queries that should be cached:

sessionFactory.getCurrentSession().createQuery("...").setCacheable(true).list();

By default, Ehcache will create separate cache regions for each entity that you configure for caching. You can change the defaults for these regions by adding the configuration to your ehcache.xml. To provide this configuration file, use this property in hibernate configuration:

<property name="net.sf.ehcache.configurationResourceName">/ehcache.xml</property>

And use below configuration to override the default configuration:

<cache
    name="com.somecompany.someproject.domain.Country"
    maxElementsInMemory="10000"
    eternal="false"
    timeToIdleSeconds="300"
    timeToLiveSeconds="600"
    overflowToDisk="true"
/>

Please note that in ehcache.xml, if eternal=”true” then we should not write timeToIdealSeconds, timeToLiveSeconds, hibernate will take care about those values
So if you want to give values manually better use eternal=”false” always, so that we can assign values into timeToIdealSeconds, timeToLiveSeconds manually.

timeToIdealSeconds=”seconds” means, if the object in the global cache is ideal, means not using by any other class or object then it will be waited for some time we specified and deleted from the global cache if time is exceeds more than timeToIdealSeconds value.

timeToLiveSeconds=”seconds” means, the other Session or class using this object or not, i mean may be it is using by other sessions or may not, what ever the situation might be, once it competed the time specified timeToLiveSeconds, then it will be removed from the global cache by hibernate.

Example application

In our example application, I have one DepartmentEntity for which I want to enable second level cache using ehcache. Lets record the changes step by step:

1) hibernate.cfg.xml

<?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">com.mysql.jdbc.Driver</property>
        <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/hibernatedemo</property>
        <property name="hibernate.connection.password">password</property>
        <property name="hibernate.connection.username">root</property>
        <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
        <property name="show_sql">true</property>
		<property name="hbm2ddl.auto">create</property>
		<property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property>
        <mapping class="hibernate.test.dto.DepartmentEntity"></mapping>
    </session-factory>
</hibernate-configuration>

2) DepartmentEntity.java

package hibernate.test.dto;

import java.io.Serializable;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;

import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;

@Entity (name = "dept")
@Table(name = "DEPARTMENT", uniqueConstraints = {
		@UniqueConstraint(columnNames = "ID"),
		@UniqueConstraint(columnNames = "NAME") })
		
@Cache(usage=CacheConcurrencyStrategy.READ_ONLY, region="department")

public class DepartmentEntity implements Serializable {
	
	private static final long serialVersionUID = 1L;

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	@Column(name = "ID", unique = true, nullable = false)
	private Integer id;
	
	@Column(name = "NAME", unique = true, nullable = false, length = 100)
	private String name;

	public Integer getId() {
		return id;
	}

	public void setId(Integer id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
}

3) HibernateUtil.java

package hibernate.test;

import java.io.File;

import org.hibernate.SessionFactory;
import org.hibernate.cfg.AnnotationConfiguration;

public class HibernateUtil 
{
	private static final SessionFactory sessionFactory = buildSessionFactory();
	 
    private static SessionFactory buildSessionFactory() 
    {
        try 
        {
            // Create the SessionFactory from hibernate.cfg.xml
            return new AnnotationConfiguration().configure(new File("hibernate.cgf.xml")).buildSessionFactory();
        }
        catch (Throwable ex) {
            // Make sure you log the exception, as it might be swallowed
            System.err.println("Initial SessionFactory creation failed." + ex);
            throw new ExceptionInInitializerError(ex);
        }
    }
 
    public static SessionFactory getSessionFactory() {
        return sessionFactory;
    }
 
    public static void shutdown() {
    	// Close caches and connection pools
    	getSessionFactory().close();
    }
}

4) TestHibernateEhcache.java

public class TestHibernateEhcache 
{	
	public static void main(String[] args) 
	{
		storeData();
		
		try
		{
			//Open the hibernate session
			Session session = HibernateUtil.getSessionFactory().openSession();
			session.beginTransaction();
			
			//fetch the department entity from database first time
			DepartmentEntity department = (DepartmentEntity) session.load(DepartmentEntity.class, new Integer(1));
			System.out.println(department.getName());
			
			//fetch the department entity again; Fetched from first level cache
			department = (DepartmentEntity) session.load(DepartmentEntity.class, new Integer(1));
			System.out.println(department.getName());
			
			//Let's close the session
			session.getTransaction().commit();
			session.close();
			
			//Try to get department in new session
			Session anotherSession = HibernateUtil.getSessionFactory().openSession();
			anotherSession.beginTransaction();
			
			//Here entity is already in second level cache so no database query will be hit
			department = (DepartmentEntity) anotherSession.load(DepartmentEntity.class, new Integer(1));
			System.out.println(department.getName());
			
			anotherSession.getTransaction().commit();
			anotherSession.close();
		}
		finally
		{
			System.out.println(HibernateUtil.getSessionFactory().getStatistics().getEntityFetchCount()); //Prints 1
			System.out.println(HibernateUtil.getSessionFactory().getStatistics().getSecondLevelCacheHitCount()); //Prints 1
			
			HibernateUtil.shutdown();
		}
	}
	
	private static void storeData()
	{
		Session session = HibernateUtil.getSessionFactory().openSession();
		session.beginTransaction();
		
		DepartmentEntity department = new DepartmentEntity();
		department.setName("Human Resource");
		
		session.save(department);
		session.getTransaction().commit();
	}
}

Output:

Hibernate: insert into DEPARTMENT (NAME) values (?)
Hibernate: select department0_.ID as ID0_0_, department0_.NAME as NAME0_0_ from DEPARTMENT department0_ where department0_.ID=?
Human Resource
Human Resource
Human Resource
1
1

In above output, first time the department is fetched from database. but next two times it is fetched from cache. Last fetch is from second level cache.

To download the sourcecode of above application, please follow below link.

Sourcecode Download

Hope you likes above article. If you got any question or suggestion, drop a comment.

Happy Learning !!

References

http://www.ehcache.org/documentation/user-guide/hibernate
https://community.jboss.org/wiki/ConfigureEhcacheAsASecondLevelCache

41 thoughts on “Hibernate EhCache configuration tutorial”

  1. Hi Lokesh

    I have executed the code given by you. I have few questions and issues

    System.out.println(HibernateUtil.getSessionfactory().getStatistics().getEntityFetchCount());
    System.out.println(HibernateUtil.getSessionfactory().getStatistics().getSecondLevelCacheHitCount());

    The above prints 0 and 2 but not 1 and 1

    Following are my questions

    1. How do i know whether the ehcache is being read and where exactly it is stored(I mean the physical location)

    diskStore path=”java.io.tmpdir”. After my program completes i dont see any object at this location.

    What i am expecting is what actually is created when we configure ehcache.xml. (Is it any object or any file. What is it )

    This is my ehcahe.xml

    This is placed in src/maim/resources folder along with hibernate.cfg.xml

      1. <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="ehcache.xsd">
        
        <diskStore path="c:\temp\cache"/>
        <cache
            name="com.pramati.dto.Department"
            maxElementsInMemory="10000"
            eternal="false"
            timeToIdleSeconds="300"
            timeToLiveSeconds="600"
            overflowToDisk="true"
        />
        
        </ehcache>
        

        The folder specified in diskStore path=”c:\temp\cache” is getting created but i don’t see any objects in that folder.

        Once my program execution is completed how do i clear the cache and make sure that next time i run this program the statistics show 1 and 1 instead of 0 and 2

          1. 
            <?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="connection.driver_class">org.postgresql.Driver</property>
                    <property name="connection.url">jdbc:postgresql://localhost:5432/hibernatexml</property>
                    <property name="connection.username">postgres</property>
                    <property name="connection.password">postgres</property>
            
            
                    <!-- JDBC connection pool (use the built-in) -->
                    <property name="connection.pool_size">1</property>
            
                    <!-- SQL dialect -->
                    <property name="dialect">org.hibernate.dialect.PostgreSQLDialect</property>
            
                    <!-- Enable Hibernate's automatic session context management -->
                    <property name="current_session_context_class">thread</property>
                    
                    <property name="cache.use_second_level_cache">true</property>
                    <property name="cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</property>
                    <property name="net.sf.ehcache.configurationResourceName">/ehcache.xml</property>
                   
                    <!-- Echo all executed SQL to stdout -->
                    <property name="show_sql">true</property>
                    <property name="hbm2ddl.auto">create</property>
            		
            			
            	<mapping resource="com/pramati/dto/Department.hbm.xml"></mapping>	
            
                </session-factory>
            
            </hibernate-configuration>
            
            
  2. Hi Lokesh,

    It is very well explained but I still have a query regarding point 7 in How second level cache works? My Question is how application knows that the db is updated directly and it has to invalidate the session.

    1. As I mentioned in point 7 that “there is no way that second level cache update itself“. So application can never know when DB got updated automatically. Either you need to tell application to invalidate it’s whole cache, OR application will do itself when ‘timeToLiveSeconds’ is passed. After this configured time, cache is re-build again whether DB was updated or not. It’s routine process.

      1. So you mean, at least for some time application will not get updated data. Isn’t it a problem which we are overlooking at? Kindly let me know if you find any way we can address this?

        1. Yes, absolutely true. For some time, application will be working with stale data/cache. Unfortunately, there is no way (If any exist then I am not aware of) to notify application/hibernate. Hibernate refresh data only when it is updated through it’s own way. If you use backdoor [direct update in DB] then how hibernate will come to know about this.

          Usually to handle such cases, in production applications I have worked on, there was an admin panel where you click “Flush All Cache” button and it will re-generate the whole cache again.

  3. ehcache or other variants are always work on 2. level cache or is it possible to configure them work on 1. level?Thanks in advance

    1. At first level, there is always hibernate default cache and you cannot even disable it. You can choose anything at only second level cache.

  4. when we set or change entity and commit the transection session cache is updated.So 2. level cache is also updated or not? if so the other sessions will still use old data because they are referring first level cache before second level maybe second level cache changes trigger to all sessions update their data from 2. level context.

  5. Hello I am just implementing Query level cache in my code using annotation and currently facing a problem ….can you please clarify that do we need to use @Cache() at the entity level if we are implementing Query Level cache only.

      1. Thanks Lokesh!

        I have create a stand alone program for ehcache. I have deleted/renamed ehcache.xml even after that i can see the cached file in java.io.tmpdir” path.

        Could you please let me know how/where to use ehcache.xml. Can i rename it/can path updated from java.io.tmpdir to any-other path.

        Appreciate your response.

  6. I am applying EHCache for Hibernate4,
    This is my configuration
    In entity,i configured like this

    @Table(name = “tbl_lookup”)
    @Cache(usage=CacheConcurrencyStrategy.READ_ONLY)

    In hibernate.cfg.xml :
    org.hibernate.cache.EhCacheProvider
    true
    true
    org.hibernate.cache.ehcache.EhCacheRegionFactory

    Ehcache.xml :

    I have add the jars : ehcache-core-2.4.5.jar,hibernate-ehcache-4.1.0.Final.jar

    Prob : For first click it getting the objects putting into 2nd level cache(ex:SecondLevelCachePutCount -30),from next click onwards it is getting the 7 objects which are there in second level cache.Now it is updating SecondLeveCacheMissCount,SecondLevelHitCount(if we observe with JConsole).It should not go to DB again.

    Please Help me……

  7. very good article written in very simple and easy to understand language. also code snippets helped to understand completely.
    Thanks a lot . You rocks

    1. A stale persistent object is no longer valid. Its fields have default values or values left over from previous transactions and should not be used. An object can also become stale when the transaction in which it was accessed is aborted because of a deadlock or another action that caused AbortException to be signaled.

      I will cover more in any future post.

  8. Sorry about my bad english.

    Firstly I want to thank for all these tutorials, secondly I followed all steps mentioned above but my output was like that :
    Output:

    Hibernate: insert into DEPARTMENT (NAME) values (?)
    Hibernate: select department0_.ID as ID0_0_, department0_.NAME as NAME0_0_ from DEPARTMENT department0_ where department0_.ID=?
    Human Resource
    Human Resource
    Human Resource
    0
    0
    the two lines :
    System.out.println(HibernateUtil.getSessionFactory().getStatistics().getEntityFetchCount());
    System.out.println(HibernateUtil.getSessionFactory().getStatistics().getSecondLevelCacheHitCount()); Print 0 instead of 1.

    would you explaining me why?

    1. Probably you have run the example more than one time. Second level cache store the information in file system and next time when you run the example, it refers from there only. So, first statement will print 0 because it never gone to database. For second statement, it should print 1. Try putting “hibernate.generate_statistics” to true in hibernate.cfg.xml.

      Also try inspect values from other methods in : http://docs.jboss.org/hibernate/orm/3.2/api/org/hibernate/stat/Statistics.html

Note:- In comment box, please put your code inside [java] ... [/java] OR [xml] ... [/xml] tags otherwise it may not appear as intended.

Leave a Reply

Your email address will not be published. Required fields are marked *


+ five = 10

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>