Hibernate Second Level Ehcache Example

About Cache (Computing):
In computing, a cache is a component that stores data so future requests for that data can be served faster; the data stored in a cache might be the results of an earlier computation, or the duplicates of data stored elsewhere. A cache hit occurs when the requested data can be found in a cache, while a cache miss occurs when it cannot. Cache hits are served by reading data from the cache, which is faster than recomputing a result or reading from a slower data store; thus, the more requests can be served from the cache, the faster the system performs. More details wikipedia

Hibernate Second Level EHCache Example

In three tire application where large number of database traffic, between application layer to data layer. To overcome with database traffic, hibernate full-fill the requirement and cache the required or specific data in hibernate cache and return data on demand so the traffic between application and the database will be reduced when the application want to access that data again and again.



















Type of Cache in Hibernate:
First-level cache
Second-level cache.

First-level cache:-
First-level cache always associates with the Session object. Hibernate uses this cache by default. First level cache is enabled by default. Here, it processes one transaction after another one, it means it wont process one transaction many times. Mainly it is reducing the number of SQL queries it needs to generate within a given transaction i.e. instead of updating after every modification done in the transaction, it updates the transaction only at the end of the transaction.

Second-level cache:-
Second level cache was introduced in hibernate 3.0. Second-level cache always associates with the Session Factory object. Second level cache works while running the transactions, in between it loads the objects at the Session Factory level, so that those objects will available to the entire application, don’t bounds to single user. Since the objects are already loaded in the cache, whenever an object is returned by the query, at that time no need to go for a database transaction. We can use directly query level cache.

Hibernate Cache Implementations:-

EHCache (Easy Hibernate Cache) (org.hibernate.cache.EhCacheProvider)
It is lightweight and that's why it is fast.
It is Easy-to-use.
It supports read-only and read/write caching.
It supports memory-based and disk-based caching.
It does not support clustering.

Note: Don't implement this if you have clustering/loadbalancer.

Hibernate Caching/Concurrency Strategies
A concurrency strategy is a way which is responsible for storing items of data in the cache and retrieving them from the cache.
Read-only
It is useful for those data which is frequently read but never updated.
It is Simple.
This is the best performer among the all caching strategy.
Advantage if this one is, It is safe for using in a cluster. Here is an example for using the read-only cache strategy.

E.g:-
<class name="com.gaurav.bean.Customer" mutable="true">
<cache usage="read-only"/>
..........................
</class>

Read-Write
It is used, when our data needs to be updated frequently.
It is having more overhead than read-only caches.
It is never used if serializable transaction isolation level is required.
In a JTA environment, for obtaining the JTA TransactionManager we must specify the property hibernate.transaction.manager_lookup_class.
To use it in a cluster the cache implementation must support locking.
Here is an example for using the read-write cache strategy.

<class name="com.gaurav.bean.Customer" .... >
<cache usage="read-write"/>
..............................
<set name="test" ... >
<cache usage="read-write"/>
................................
</set>
</class>

Nonstrict read-write
It is needed if the application needs to update data rarely.
we must specify hibernate.transaction.manager_lookup_class to use this in a JTA environment .
Here is an example for using the nonstrict read-write cache strategy.

<class name="com.gaurav.bean.Customer" .... >
<cache usage=" nonstrict-read-write"/>
...............................................
</class>

Transactional
It supports only transactional cache providers such as JBoss TreeCache.
It is only used in JTA environment.

Configuring Cache in Hibernate
We can use hibernate.cfg.xml file for the cache configuration. A typical configuration file is shown below.

<hibernate-configuration>
    <session-factory>
        .............................
        <property name="cache.provider_class">org.hibernate.cache.EhCacheProvider</property>
        .............................
    </session-factory>
</hibernate-configuration>
Hibernate cache tag element
The <cache> element has the following attributes:
<cache
    usage=" caching strategy"
    region="RegionName"
    include="all | non-lazy"/>
usage (mandatory) specifies the caching strategy: transactional, read-write, nonstrict-read-write or read-only.
region (optional) specifies the name of the second level cache region .
include (optional) non-lazy specifies that properties of the entity mapped with lazy=”true” may not be cached when attribute-level lazy fetching is enabled.

Hibernate queries Caching
Hibernate is providing a property through which we can cache the queries. Many times it is happening that some queries are running frequently with same parameters, we can cache those queries. Just We have to set hibernate.cache.use_query_cache to true by calling Query.setCacheable(true) for enabling the query cache.
    For storing the most recent updates.
    For storing the results.
Query cache always used second-level cache only. Queries wont cached by default. Below is an example implementation of query cache.

        Session session = SessionFactory.openSession();
        Query query = session.createQuery("FROM CUSTOMER");
        query.setCacheable(true);
        query.setCacheRegion("customer");
        List users = query.list();
        SessionFactory.closeSession();

Note: - This(query.setCacheRegion("customer")) code uses the method to tell Hibernate to store and look for the query in the customer area of the cache.
 
hibernate.cache.use_query_cache property We need to set in the hibernate.cfg.xml file to true like below :

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


Then, we can use the setCacheable() method on any query we wish to cache.

Hibernate second level cache example
Considering you have in your System:
Java
Hibernate jar
Mysql
Any Editor

Required files are as below:- 
Customer.java
Customer.hbm.xml
ehcache.xml
hibernate.cfg.xml
log4j.properties
HibernateSendLevelCacheTest.java

Customer.java
package com.tutorialbyexample.bean;

public class Customer {

    private int customerId;
    private String customerName;
    private double expenditure;

    /**
     * @return the customerId
     */
    public int getCustomerId() {
        return customerId;
    }

    /**
     * @param customerId
     *            the customerId to set
     */
    public void setCustomerId(int customerId) {
        this.customerId = customerId;
    }

    /**
     * @return the customerName
     */
    public String getCustomerName() {
        return customerName;
    }

    /**
     * @param customerName
     *            the customerName to set
     */
    public void setCustomerName(String customerName) {
        this.customerName = customerName;
    }

    /**
     * @return the expenditure
     */
    public double getExpenditure() {
        return expenditure;
    }

    /**
     * @param expenditure
     *            the expenditure to set
     */
    public void setExpenditure(double expenditure) {
        this.expenditure = expenditure;
    }

}


Customer.hbm.xml

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping>
    <class name="com.gaurav.bean.Customer" table="customer">
        <cache usage="read-only" />
        <id name="customerId" column="CUSTOMER_ID" />
        <property name="customerName" column="CUSTOMER_NAME" length="50" />
        <property name="expenditure" column="TOTAL_EXPENDITURE" />
    </class>
</hibernate-mapping>

ehcache.xml

<?xml version="1.0"?>

<ehcache>
    <defaultCache maxElementsInMemory="100" eternal="false"
        timeToIdleSeconds="120" timeToLiveSeconds="200" />
    <cache name="com.gaurav.bean.Customer" maxElementsInMemory="100" eternal="false"
        timeToIdleSeconds="5" timeToLiveSeconds="200" />
</ehcache>

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/hibsecondlevelcachedb</property>
        <property name="hibernate.connection.username">root</property>
        <property name="connection.password">root</property>
        <property name="cache.provider_class">org.hibernate.cache.EhCacheProvider</property>
        <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
        <property name="show_sql">true</property>
        <property name="hibernate.hbm2ddl.auto">update</property>
        <mapping resource="Customer.hbm.xml"></mapping>
    </session-factory>
</hibernate-configuration>

HibernateSendLevelCacheTest.java

package com.tutorialbyexample.impl.test;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

import com.tutorialbyexample.bean.Customer;

public class HibernateSendLevelCacheTest {

    public static void main(String[] args) {

        Configuration cfg = new Configuration();
        cfg.configure("hibernate.cfg.xml");

        SessionFactory sessionFactory = cfg.buildSessionFactory();
        Session firstSession = sessionFactory.openSession();
        Object object1 = firstSession.load(Customer.class, new Integer(1));

        Customer customer1 = (Customer) object1;

        System.out.println("Object Loaded first and here customer name is-->"
                + customer1.getCustomerName());
        System.out.println("Object Loaded first and here customer id is-->"
                + customer1.getCustomerId());
        System.out
                .println("Object Loaded first and here customer expenditure is-->"
                        + customer1.getExpenditure());
        System.out.println("***** Object is loaded here *****");

        firstSession.clear();
        firstSession.close();

        System.out.println("Thread.sleep called Status-Waiting......");

        try {
            Thread.sleep(20000);
        } catch (Exception e) {
        }

        System.out
                .println("----------------------------Waiting---------------------------------------------------------");

        System.out.println("20 seconds compelted during wait");

        Session secondSession = sessionFactory.openSession();
        Object object2 = secondSession.load(Customer.class, new Integer(1));

        System.out
                .println("-------------------------------------------------------------------------------------");

        Customer customer2 = (Customer) object2;

        System.out
                .println("Second time object loaded and here customer name is -->"
                        + customer2.getCustomerName());
        System.out
                .println("Second time object loaded and here customer id is-->"
                        + customer1.getCustomerId());
        System.out
                .println("Second time object loaded and here customer expenditure is-->"
                        + customer1.getExpenditure());
        System.out.println("***** Object loaded from the database *****");

        secondSession.close();

        Session thirdSession = sessionFactory.openSession();

        Object object3 = thirdSession.load(Customer.class, new Integer(1));

        System.out
                .println("-------------------------------------------------------------------------------------");

        Customer customer3 = (Customer) object3;

        System.out
                .println("Third time object loaded and here customer name is-->"
                        + customer3.getCustomerName());
        System.out
                .println("Third time object loaded and here customer id is-->"
                        + customer1.getCustomerId());
        System.out
                .println("Third time object loaded and here customer expenditure is-->"
                        + customer1.getExpenditure());
        System.out
                .println("***** Object loaded from global cache successfully *****");

        thirdSession.close();

        sessionFactory.close();
    }

}


All the objects those are passed to methods save(), update() or saveOrUpdate() or those you get from load(), get(), list(), iterate() or scroll() will be saved into cache.
evict() is used to delete the object from cache.
flush() is used to synchronize the object with database.
Session.clear() used to delete all objects from the cache .
contains() used to find whether the object belongs to the cache or not.
If the query wants to force a refresh of its query cache region, we should call Query.setCacheMode(CacheMode.REFRESH).


Reference from:
1. http://hibernate.org/orm/

1 comment: