To improve the performance the enterprise applications often
it’s required to cache the data from database somewhere in the RAM or hard
drive. This will boost the response time of application as most of the data accessed
from the cached location instead of hitting the database for each and every
request. For example suppose application displays configuration related data
which most of the time will not changes often. This data can be cached for the very
first request and displayed later.Now we can think of Hibernate how it manages caching in
application.
Hibernate supports two types of caching:
1.
First level caching and
2.
Second level caching
First level caching:
By default hibernate API supports First level caching by
session. But scope of cached data limited for that session only.
Second Level Cache:
Session Factory holds the second level cache data. Cached
data will be available for entire application. Configurations required to enable
the second level cache. There are different vendors available in the market to
provide caching implementation.
1.
EH cache (Easy Hibernate Cache)
2.
Swaram Cache
3.
JBoss Cache
4.
OS Cache
How does Second Level Cache Works?
Entity objects never cached instead entity data will be
cached in the system. The data is stored
in dehydrated format and it looks like hash map where key will be entity id and
values will be list of primitive values.
*-----------------------------------------*
| Person Data Cache |
|-----------------------------------------|
|
1 -> [ "Suresh" , "Q" , "Public" , null ] |
|
2 -> [ "Mahesh" , "D" , "Public" , 1 ] |
|
3 -> [ "Ramesh" , "N" , "Public" , 1 ] |
*-----------------------------------------*
Sample EHcache xml file:
<?xml version="1.0" encoding="UTF-8"?>
configuration can be reffered by below settings.
<property name="net.sf.ehcache.configurationResourceName">/Ehcache.xml</property>
Sample EHcache xml file:
<?xml version="1.0" encoding="UTF-8"?>
xsi:noNamespaceSchemaLocation="ehcache.xsd" updateCheck="true"
monitoring="autodetect" dynamicConfig="true">
<diskStore path="java.io.tmpdir/ehcache" />
<defaultCache maxEntriesLocalHeap="10000" eternal="false"
timeToIdleSeconds="120" timeToLiveSeconds="120" diskSpoolBufferSizeMB="30"
maxEntriesLocalDisk="10000000" diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU" statistics="true">
<persistence strategy="localTempSwap" /></defaultCache>
<cache name="com.gps.model.FoodItems" maxEntriesLocalHeap="10000" eternal="false"
timeToIdleSeconds="5" timeToLiveSeconds="10">
<persistence strategy="localTempSwap" />
</cache>
<cache name="org.hibernate.cache.internal.StandardQueryCache"
maxEntriesLocalHeap="5" eternal="false" timeToLiveSeconds="10">
<persistence strategy="localTempSwap" />
</cache>
<cache name="org.hibernate.cache.spi.UpdateTimestampsCache"maxEntriesLocalHeap="5000" eternal="true">
<persistence strategy="localTempSwap" />
</cache>
</ehcache>
configuration can be reffered by below settings.
<property name="net.sf.ehcache.configurationResourceName">/Ehcache.xml</property>
How does Query Cache Works?
Conceptually query cache works like hash map where key will be query text along with parameter values and value will be list of entity IDs that match the query.
Conceptually query cache works like hash map where key will be query text along with parameter values and value will be list of entity IDs that match the query.
*----------------------------------------------------------*
| Query Cache
| Value
KEY |
|----------------------------------------------------------|
|
["from dept1 where id=?", ["1"] ] -> [1, 2] ] |
*----------------------------------------------------------*
Some queries don't return entities instead of they
return only primitive values. In those cases the values themselves will be
stored in the query cache. The query cache gets populated when a cacheable
JPQL/HQL query gets executed.
Note:If EHCache settings are not provided default settings will considered.
Database for Demo purpose:
Steps for Query Level Cache
Step 1: Create an entity DepartmentEntity
and annotate as highlighted in blue.
package hibernate.test.dto;
import java.io.Serializable;
import javax.persistence.Cacheable;
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;
import java.io.Serializable;
import javax.persistence.Cacheable;
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 = "dept1")
@Cacheable
@Table(name = "DEPARTMENT", uniqueConstraints = {
@UniqueConstraint(columnNames = "ID"),
@UniqueConstraint(columnNames = "NAME") })
@Cacheable
@Table(name = "DEPARTMENT", uniqueConstraints = {
@UniqueConstraint(columnNames = "ID"),
@UniqueConstraint(columnNames = "NAME") })
@Cache(usage=CacheConcurrencyStrategy.READ_ONLY,
region="CRDEPT")
public class DepartmentEntity implements Serializable {
private static final long serialVersionUID
= 1L;
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
@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;
}
}
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
@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;
}
}
Step 2: Create hibernate
configuration file.
Below are the properties needs to be explicitly declared in
the configuration file.hibernate.cache.provider_class----->which cache provider needs to be allowed
hibernate.cache.use_query_cache------>explicitly allow query cache
<property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property><property name="hibernate.cache.use_query_cache">true</property>
<?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">org.h2.Driver</property>
<property
name="hibernate.connection.url">jdbc:h2:tcp://localhost/server~/test</property>
<property
name="hibernate.connection.password">password</property>
<property
name="hibernate.connection.username">sa</property>
<property
name="hibernate.dialect">org.hibernate.dialect.H2Dialect</property>
<property
name="show_sql">true</property>
<property
name="hbm2ddl.auto">update</property>
<property
name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property>
<property
name="hibernate.cache.use_query_cache">true</property>
<mapping
class="hibernate.test.dto.DepartmentEntity"></mapping>
</session-factory>
</hibernate-configuration>
Step 3: executing queries.
Set the query as catchable. Here HQL is ---->from dept1. Once the query is executed result will be cached and if same query
is executed in second session no SQL sent to DB.
// Open the hibernate session
Session
session = HibernateUtil.getSessionFactory().openSession();
session.beginTransaction();
//Query
q = session.createQuery("from dept1 where ID=:dpt_id
").setParameter("dpt_id", 1);
Query
q = session.createQuery("from dept1");
//Query
q = session.createQuery("select count(*)from dept1");
q.setCacheable(true);
//q.setCacheRegion("dept1");
List<DepartmentEntity>
listobjects1 = null;
listobjects1
= q.list();
Iterator<DepartmentEntity>
it1 = listobjects1.iterator();
System.out.println("First
Session Data:");
while
(it1.hasNext())
{
DepartmentEntity
departmentEntity = (DepartmentEntity) it1.next();
System.out.println("Department
Names-->"+ departmentEntity.getName());
}
session.getTransaction().commit();
session.close();
Session
session1 = HibernateUtil.getSessionFactory().openSession();
session1.beginTransaction();
//Query
q1 = session1.createQuery("from dept1 where
ID=:dpt_id").setParameter("dpt_id", 50);
Query
q1 = session1.createQuery("from dept1");
//Query
q1 = session1.createQuery("select count(*)from dept1");
q1.setCacheable(true);
//q1.setCacheRegion("dept1");
List<DepartmentEntity>
listobjects11 = null;
listobjects11
= q1.list();
Iterator<DepartmentEntity>
it11 = listobjects11.iterator();
System.out.println("Second
Session Data:");
while
(it11.hasNext())
{
DepartmentEntity departmentEntity =
(DepartmentEntity) it11.next();
System.out.println("Department
Names-->"+ departmentEntity.getName());
}
session1.getTransaction().commit();
session1.close();
Result:
Result:
As we can see only one time SQL sent to DB.
log4j:WARN No appenders could be found
for logger (org.hibernate.cfg.annotations.Version).
log4j:WARN Please initialize the log4j
system properly.
Hibernate:
select department0_.ID as ID0_, department0_.NAME as NAME0_ from DEPARTMENT
department0_
First Session Data:
Department Names--> Human Resource
Department Names--> Dev
Department Names--> QA
Department Names--> QA_1
Second Session Data:
Department Names--> Human Resource
Department Names--> Dev
Department Names--> QA
Department Names--> QA_1