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
.