Tuesday, 23 July 2013

Hibernate second level cache using Ehcache


Hibernate is compatible with many second-level cache providers. One of the best solution is Ehcache.
We create a sample project and configure second-level cache with hibernate using Maven. Here we use Hibernate 4.1 and Hibernate Ehcache 4.1
So the pom.xml is look like this
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.haresh</groupId>
<artifactId>hibernate-tutorials</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>HibernateTutorial</name>


<dependencies>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>4.1.12.Final</version>
</dependency>

<!-- Hibernate uses jboss-logging for logging, for the tutorials we will
use the sl4fj-simple backend -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.6.1</version>
</dependency>

<!-- The tutorials use JUnit test cases to illustrate usage -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
</dependency>

<!-- The tutorials use the H2 in-memory database -->

<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.9</version>
</dependency>

<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-ehcache</artifactId>
<version>4.1.1.Final</version>
</dependency>

<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.2.145</version>
</dependency>
</dependencies>
</project>


Now have to create a Hibernate-configuration File. hibernate.cfg.xml.
the Default configuration of hibernate.cfg.xml is as below,

<!-- Disable the second-level cache -->
<property name="cache.provider_class">org.hibernate.cache.internal.NoCacheProvider</property>


Here we have to change the configuration to enable second-level cache. I mention that we are using 4.x version of Hibernate.
So, In our case we have to add two more property in configuration file. One is for enable the cache, it's a boolean value, this value tell to hibernate that the second-level cache is enable.
And Second-one is Provider Class, which tell to hibernate that the Second-level cache is used through this provider. Below is the configuration.


<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">

<hibernate-configuration>
<session-factory>
<!-- Database connection settings -->
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.password"></property>
<property name="hibernate.connection.url">jdbc:mysql://localhost:3306/test</property>
<property name="hibernate.connection.username">root</property>
<!-- JDBC connection pool (use the built-in) -->
<property name="connection.pool_size">1</property>

<!-- SQL dialect -->
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>

<!-- Enable the second-level cache -->
<property name="hibernate.cache.use_second_level_cache">true</property>
<property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</property>

<!-- Echo all executed SQL to stdout -->
<property name="show_sql">true</property>

<!-- Drop and re-create the database schema on startup -->
<property name="hbm2ddl.auto">update</property>

<!-- Names the annotated entity class -->
<mapping class="com.haresh.model.Event"/>

</session-factory>

</hibernate-configuration>
Now we have to create an entity. Which have annotation for ORM. And we add another annotation to this entity for cache.
At the top of Class and below the @Entity annotation we add two more annotation, one is @Cacheable which tell to hibernate that the entity is enable for Cache. And the Second one and Most important thing is to add Cache strategy, Here we add READ-ONLY. The Object is only in read mode while it's a cache-able, there is no insert/update operation perform event after the change of State of Object. Hibernate have no worry to manage Object when it's state change. In READ-WRITE the Object's data is update then the Cache is also update simultaneously.
package com.haresh.model;

import java.util.Date;

import javax.persistence.Cacheable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;

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

@Entity
@Cacheable
@Cache(usage = CacheConcurrencyStrategy.READ_ONLY)
@Table( name = "EVENTS" )
public class Event {
private Long id;

private String title;
private Date date;

public Event() {
// this form used by Hibernate
}

public Event(String title, Date date) {
// for application use, to create new events
this.title = title;
this.date = date;
}

@Id
@GeneratedValue(generator="increment")
@GenericGenerator(name="increment", strategy = "increment")
public Long getId() {
return id;
}

private void setId(Long id) {
this.id = id;
}

@Temporal(TemporalType.TIMESTAMP)
@Column(name = "EVENT_DATE")
public Date getDate() {
return date;
}

public void setDate(Date date) {
this.date = date;
}

public String getTitle() {
return title;
}

public void setTitle(String title) {
this.title = title;
}
@Override
public String toString() {
return "[id = "+id+", title = "+title+"]";
}
}


Now let's test the code, We are trying to fetch a Record from Database of Event having ID equal 1. In a normal case when we are trying to fetch record from Hibernate Session, it's traverse to the database for each new query in a every new open session.
Here It's traverse once to database and in second query it's look in to cache and get data immediatly. For ex.


Session session = HibernateUtil.getSessionFactory().openSession();
session.getTransaction().begin();
session.get(Event.class, 1l);
session.getTransaction().commit();
session.close();
// Again try to fatch data for the same Event Id,It retrive from Cache
session = HibernateUtil.getSessionFactory().openSession();
session.getTransaction().begin();
session.get(Event.class, 1l);
session.getTransaction().commit();
session.close();


In console it print only one select query rather than two select query. The Second fetch was from Cache.
Hibernate: select event0_.id as id1_0_0_, event0_.EVENT_DATE as EVENT2_0_0_, event0_.title as title3_0_0_ from EVENTS event0_ where event0_.id=?


Hibernate Cache Strategy.(http://docs.jboss.org/hibernate/orm/4.1/devguide/en-US/html_single/#d5e1364)
read-only
A read-only cache is good for data that needs to be read often but not modified. It is simple, performs well, and is safe to use in a clustered environment.
nonstrict read-write
Some applications only rarely need to modify data. This is the case if two transactions are unlikely to try to update the same item simultaneously. In this case, you do not need strict transaction isolation, and 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, ensore that the transaction is complete before you call Session.close() or Session.disconnect().
read-write
A read-write cache is appropriate for an application which needs to update data regularly. Do not use a read-write strategy if you need serializable transaction isolation. In a JTA environment, specify a strategy for obtaining the JTA TransactionManager by setting the property hibernate.transaction.manager_lookup_class. In non-JTA environments, be sure the transaction is complete before you call Session.close() or Session.disconnect().
transactional
The transactional cache strategy provides support for transactional cache providers such as JBoss TreeCache. You can only use such a cache in a JTA environment, and you must first specify hibernate.transaction.manager_lookup_class.

No comments:

Post a Comment

Infinidb _CpNoTf_ problem

infinidb table with a varchar column insert string as a '_CpNoTf_' while using Cpimport. The Problem is occured if inserted string ...