Friday, June 12, 2015

Use Hibernate level two cache with ehcache – hello world example

No doubt that well designed second level cache will greatly increase persistence performance. Hibernate clearly define the usage of 2nd level cache, but leave the cache implementation to other cache providers.  In this article ehcache is used as the second level cache provider.

0. What you need

  • JDK 1.7+
  • Spring 4.1.0.RELEASE
  • Hibernate 4.3.4.Final

1. Configure maven pom

<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.shengwang.demo</groupId>
<artifactId>spring-persistence-hibernate-cache</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>

<name>spring-persistence-hibernate-cache</name>
<url>http://maven.apache.org</url>

<properties>
<!-- Spring version -->
<spring.version>4.1.0.RELEASE</spring.version>

<!-- Hibernate version -->
<hibernate.version>4.3.4.Final</hibernate.version>
</properties>

<dependencies>
<!-- Spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>${spring.version}</version>
</dependency>

<!-- Hibernate -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>${hibernate.version}</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-ehcache</artifactId>
<version>${hibernate.version}</version>
</dependency>

<!-- Database driver-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.2.2</version>
</dependency>

</dependencies>

<!-- Use java 1.7 -->
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.5.1</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
</plugins>
</build>
</project>

The dependencies in pom file belong to spring, hibernate and database connection driver.  Beside the normal needed dependencies, the  hibernate-ehcache is added to serve as 2nd level cache provider, which has the same version as the hibernate-core.


2. Add @Cache to entity class


In this demo, there are 2 entity classes, Client and PurchaseOrder. One client can has many orders, so it’s a one-to-many relation.


First is the PurchaseOrder entity class.

package com.shengwang.demo.model;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;

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


@Entity
@Table(name="purchase_order")
@Cache(usage=CacheConcurrencyStrategy.READ_ONLY)
public class PurchaseOrder {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="order_id")
private int orderId;

@Column(name="order_desc")
private String orderDesc;

@ManyToOne(cascade=CascadeType.PERSIST)
@JoinColumn(name="client_id")
private Client client;

public int getOrderId() {
return orderId;
}

public String getOrderDesc() {
return orderDesc;
}

public Client getClient() {
return client;
}

public void setOrderId(int orderId) {
this.orderId = orderId;
}

public void setOrderDesc(String orderDesc) {
this.orderDesc = orderDesc;
}

public void setClient(Client client) {
this.client = client;
}
}

This is just an ordinary entity class, but with just one extra annotation @Cache(usage=CacheConcurrencyStrategy.READ_ONLY) . That tells the hibernate the entity can be cached.


Then the client entity class.

package com.shengwang.demo.model;

import java.util.HashSet;
import java.util.Set;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;

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


@Entity
@Table(name="client")
@Cache(usage=CacheConcurrencyStrategy.READ_ONLY)
public class Client {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="client_id")
private int clientId;

@Column(name="client_name")
private String clientName;

//------------------------------------
// Caution: need to cache collections
//------------------------------------
@OneToMany(mappedBy="client",cascade=CascadeType.PERSIST)
@Cache(usage=CacheConcurrencyStrategy.READ_ONLY)
private Set orders = new HashSet();


public int getClientId() {
return clientId;
}
public String getClientName() {
return clientName;
}

public Set getPurchaseOrders() {
return orders;
}
public void setClientId(int clientId) {
this.clientId = clientId;
}
public void setClientName(String clientName) {
this.clientName = clientName;
}
public void setPurchaseOrders(Set orders) {
this.orders = orders;
}
}

Compare to the PurchaseOrder entity class, the Client entity not only add @Cache annotation before the class definition like the above one, but also add @Cache annotaion before the collections elements. Because by default the collection fields of the entity are not cached. For most of the time collections do need to be cached to get performance improvement.


3. Configure Spring


In spring configuration file, enable the second level cache and setup the ehcahe as the cache provider.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.0.xsd">

<!-- scan for all spring beans -->
<context:component-scan base-package="com.shengwang.demo" />

<!-- enable the configuration of transactional behavior based on annotations -->
<tx:annotation-driven transaction-manager="transactionManager"/>

<!-- persistence -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/spring" />
<property name="username" value="root" />
<property name="password" value="IHave1Dream!" />
</bean>

<bean id="sessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>

<!-- enable 2nd level cache -->
<prop key="hibernate.cache.use_second_level_cache">true</prop>

<!-- setup 2nd level cache -->
<prop key="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory </prop>
<prop key="net.sf.ehcache.configurationResourceName">/ehcache.xml</prop>

</props>
</property>
<!-- set auto scan the Entity, otherwise you will get 'Unknown entiy' error -->
<property name="packagesToScan" value="com.shengwang.demo.model" />
</bean>

<bean id="transactionManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager"
p:sessionFactory-ref="sessionFactory">
</bean>

</beans>

In the session factory bean configuration, hibernate.cache.use_second_level_cache set to true to enable 2nd level cache. The cache providers set to ehcache using hibernate.cache.region.factory_class property. Also the ehcache configuration file set to ehcache.xml, which will be shown next.


4. Configure ehcache


The ehcache.xml is ehcache configuration file, which looks like this.

<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="ehcache.xsd" updateCheck="true" monitoring="autodetect" dynamicConfig="true">

<cache name="demoEntityCache"
maxEntriesLocalHeap="10000"
maxEntriesLocalDisk="1000"
eternal="false"
diskSpoolBufferSizeMB="20"
timeToIdleSeconds="300" timeToLiveSeconds="600"
transactionalMode="off">
<persistence strategy="localTempSwap" />
</cache>

<defaultCache maxEntriesLocalHeap="0" eternal="false" timeToIdleSeconds="1200" timeToLiveSeconds="1200">
</defaultCache>
</ehcache>

Now the hibernate level 2 cache is ready to serve.  In the next article here, several persistence operation will be made to verify the level two cache’s effect.

0 comments:

Post a Comment

Powered by Blogger.

About The Author

My Photo
Has been a senior software developer, project manager for 10+ years. Dedicate himself to Alcatel-Lucent and China Telecom for delivering software solutions.

Pages

Unordered List