Hibernate EhCache Configuration Tutorial

Learn to configure and use the Ehcache library as the second-level cache provider for the hibernate application. This tutorial works with Hibernate 5.x and Ehcache 2.x version.

1. Second-level Cache – A Quick Recap

We have already learned about the role of the second-level cache in hibernate performance. Let’s quickly go through the basics to begin:

  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 the 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 a 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.

2. EhCache Library

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

Alternatively, we can include the latest version of ehcache from the Maven site:

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

3. Enabling EhCache

To configure ehcache, we need to do two steps:

  • Configure Hibernate to enable second-level caching
  • Specify the second-level cache provider as ehcache

3.1. Hibernate 4.x and 5.x

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

3.2 Hibernate 3.3 and Above

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

3.3. Hibernate 3.2 and Below

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

4. Making the Entities Cacheable

To make an entity cacheable, there are two ways.

  • If you are using hbm.xml files then use below configuration:
<class name="com.application.entity.DepartmentEntity" table="...">
    <cache usage="read-write"/>
</class>
  • Otherwise, if you are using annotations, use either @Cache or @Cacheable annotation. Note that @Cache is the Hibernate cache interface and @Cacheable is the JPA cache interface.
    @Cacheable will only work if the caching element(persistence.xml) is set to ENABLE_SELECTIVE or DISABLE_SELECTIVE.
@Entity
@Cache(usage=CacheConcurrencyStrategy.READ_ONLY, region="department")
public class DepartmentEntity implements Serializable 
{
  //code
}

For both types of configurations, the caching strategy can be of the 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.

5. Enabling Query Cache

We can also enable query caching. To do so configure it in the hbm.xml:

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

and where queries are defined in the 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 we configure for caching. We 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 the 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 attribute is set to true then we should not write timeToIdealSeconds, timeToLiveSeconds, hibernate will take care of these 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 used by any other class or object then it will wait for the time we specified and get deleted from the global cache if time exceeds more than timeToIdealSeconds value.
  • timeToLiveSeconds=”seconds” means, the other Session or class using this object or not, I mean maybe it is using by other sessions or may not, whatever the situation might be, once it competed the time specified timeToLiveSeconds, then it will be removed from the global cache by hibernate.

6. Demo

In our example application, I have one DepartmentEntity for which I want to enable second-level cache using Ehcache. Let’s record the changes step by step:

6.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>

6.2. DepartmentEntity.java

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;
  }
}

6.3. HibernateUtil.java

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();
    }
}

6.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();
  }
}
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 the above output, the first time the department is fetched from the database. but the next two times it is fetched from cache. The last fetch is from the second-level cache.

Happy Learning !!

Was this post helpful?

Join 7000+ Awesome Developers

Get the latest updates from industry, awesome resources, blog updates and much more.

* We do not spam !!

38 thoughts on “Hibernate EhCache Configuration Tutorial”

  1. can we apply caching for relations like onetone, onetomany … in JPA,if applied what is the behaviour of caching will it work for all relations for read-write strategy?

    Reply
  2. Thank you for detailed tutorial.
    i would like to post one update regarding configuration in hiernate.cfg.xml.

    From version 4.x you should use following for hibernate.cache.region.factory_class property.

    <property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</property>
    
    Reply
  3. Hi,
    I have 10 level of inheritance and some of them has-a relation with some entities (with one to one and one to many)
    which are lazy loading by default but i want to load eagerly, but for lazy loading query caching is working but for eager loading its hitting db always.kindly reply

    Reply
  4. 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

    Reply
      • <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

        Reply
          • 
            <?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>
            
            
  5. 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.

    Reply
    • 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.

      Reply
      • 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?

        Reply
        • 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.

          Reply
          • This API would remove all entities for a specific cache region as said here there should be some button to flush out cache entities.

          • Also, the application which I work on a think wrapper written around DB which sends a notification(model configuration needed for mentioning the table) if any changes happen on the table. Logic is build which receives the event and updates the cache

  6. 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

    Reply
  7. 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.

    Reply
  8. 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.

    Reply
      • 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.

        Reply
  9. 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)
     

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

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

    Please Help me……

    Reply
    • 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.

      Reply
  10. Sorry about my bad English.

    Firstly I want to thank you 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 explain to me why?

    Reply

Leave a Comment

HowToDoInJava

A blog about Java and related technologies, the best practices, algorithms, and interview questions.