Second Level OSCache example in Hibernate

This tutorial will sow how we can configure second level cache using OSCache in Hibernate step by step. We know that there are three types of caching mechanism in Hibernate such as First Level – Session, Second Level – SessionFactory and Query Level – SessionFactory.

For more information on First Level, Second Level and Query Level please go through Hibernate Caching strategy.
Prerequisites

MySQL Database 5.5
JDK 1.6
MySQL Connector jar
Hibernate jars
Hibernate OSCache jar files
Create a MySQL table called cd

CREATE TABLE `cd` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `title` varchar(100) COLLATE latin1_general_ci NOT NULL,
  `artist` varchar(100) COLLATE latin1_general_ci NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=23 DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci;

 
Insert some data into the table

insert  into `cd`(`id`,`title`,`artist`) values
(4,'CD Caching','Soumitra'),
(20,'CD Caching','Soumitra'),
(21,'CD Caching','Soumitra'),
(22,'CD Caching','Soumitra');

 
Create POJO and hibernate mapping file for the database table
CD.java

package in.webtuts.domain;
import java.io.Serializable;
/**
 * @author admin
 *
 */
public class CD implements Serializable {
    /**
     *
     */
    private static final long serialVersionUID = 1L;
    Long id;
    String title;
    String artist;
    public CD() {
    }
    public CD(Long id, String title, String artist) {
        this.id = id;
        this.title = title;
        this.artist = artist;
    }
    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    public String getTitle() {
        return title;
    }
    public void setTitle(String title) {
        this.title = title;
    }
    public String getArtist() {
        return artist;
    }
    public void setArtist(String artist) {
        this.artist = artist;
    }
}

 

The above pojo file is simple and easy to use. It just declares the attributes same as in the database table.

mapping file – CD.hbm.xml

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
    <class name="in.webtuts.domain.CD" table="cd" catalog="cdcol">
        <cache usage="read-write" />
        <id name="id" type="java.lang.Long">
            <column name="id" />
            <generator class="identity" />
        </id>
        <property name="title" type="string">
            <column name="title" length="200" />
        </property>
        <property name="artist" type="string">
            <column name="artist" length="200" />
        </property>
    </class>
</hibernate-mapping>

 

The mapping file is simple and easy to understand. In the above mapping file I have used

<cache usage="read-write" />

 

use this strategy for read-mostly data where it is critical to prevent stale data in concurrent transactions,in the rare case of an update.

Now we need to configure hibernate to connect the database table as well as to use the second level cache for OSCache. Below is the hibernate.cfg.xml file

<?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.dialect">org.hibernate.dialect.MySQLDialect</property>
        <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
        <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/cdcol?zeroDateTimeBehavior=convertToNull</property>
        <property name="hibernate.connection.username">root</property>
        <property name="hibernate.current_session_context_class">thread</property>
        <property name="hibernate.query.factory_class">org.hibernate.hql.classic.ClassicQueryTranslatorFactory</property>
        <property name="hibernate.show_sql">true</property>
        <property name="hibernate.format_sql">true</property>
        <property name="hibernate.generate_statistics">true</property>
        <property name="hibernate.cache.use_structured_entries">true</property>
        <property name="hibernate.cache.use_second_level_cache">true</property>
        <property name="hibernate.cache.use_query_cache">true</property>
        <property name="hibernate.cache.provider_class">org.hibernate.cache.OSCacheProvider</property>
        <mapping resource="in/webtuts/domain/CD.hbm.xml" />
    </session-factory>
</hibernate-configuration>

 

If you need the description for each of the properties in hibernate.cfg.xml file then you can go through EHCache in Hibernate.

Notice in the above configuration file, the cache provider class is different from what we used for EHCache in Hibernate.

Now we will create HibernateUtil.java file which will give us the Singleton SessionFactory.

package in.webtuts.utils;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.AnnotationConfiguration;
/**
 * Hibernate Utility class with a convenient method to get Session Factory
 * object.
 *
 * @author admin
 */
public class HibernateUtil {
    private static final SessionFactory sessionFactory;
    static {
        try {
            // Create the SessionFactory from standard (hibernate.cfg.xml)
            // config file.
            sessionFactory = new AnnotationConfiguration().configure()
                    .buildSessionFactory();
        } catch (Throwable ex) {
            // Log the exception.
            System.err.println("Initial SessionFactory creation failed." + ex);
            throw new ExceptionInInitializerError(ex);
        }
    }
    public static SessionFactory getSessionFactory() {
        return sessionFactory;
    }
}

 
The above class is simple and easy to understand.
Next we write the CDTest.java for testing our work.

package in.webtuts.test;
import in.webtuts.domain.CD;
import in.webtuts.utils.HibernateUtil;
import org.hibernate.Session;
import org.hibernate.Transaction;
public class CDTest {
    public static void main(String[] args) {
        Session sess = null;
        Session dbSession1 = null;
        Session dbSession2 = null;
        Session newSession = null;
        Transaction tran = null;
        Transaction dbTransaction1 = null;
        Transaction dbTransaction2 = null;
        Transaction newTransaction = null;
        System.out.println("Fetch count from DB: "
                + HibernateUtil.getSessionFactory().getStatistics()
                        .getEntityFetchCount());
        System.out.println("Fetch count from second-level session: "
                + HibernateUtil.getSessionFactory().getStatistics()
                        .getSecondLevelCacheHitCount());
        System.out.println("Miss count from second-level session: "
                + HibernateUtil.getSessionFactory().getStatistics()
                        .getSecondLevelCacheMissCount());
        System.out.println("Put count from second-level session:"
                + HibernateUtil.getSessionFactory().getStatistics()
                        .getSecondLevelCachePutCount());
        System.out.println();
        try {
            // save the new CD
            sess = HibernateUtil.getSessionFactory().openSession();
            tran = sess.beginTransaction();
            CD cd = new CD();
            cd.setTitle("CD Caching");
            cd.setArtist("Soumitra");
            sess.save(cd);
            tran.commit();
            sess.close();
            dbSession1 = HibernateUtil.getSessionFactory().openSession();
            dbTransaction1 = dbSession1.beginTransaction();
            // load the CD from database table
            CD cd1 = (CD) dbSession1.load(CD.class, 4l);
            System.out.println("CD Loaded from DB - title: " + cd1.getTitle());
            System.out.println();
            // load the CD from first-level Session
            CD cd2 = (CD) dbSession1.load(CD.class, 4l);
            System.out.println("CD Loaded from First-level session - title: "
                    + cd2.getTitle());
            System.out.println();
            dbTransaction1.commit();
            dbSession1.close();
            System.out.println("Fetch count from DB: "
                    + HibernateUtil.getSessionFactory().getStatistics()
                            .getEntityFetchCount());
            System.out.println("Fetch count from second-level session: "
                    + HibernateUtil.getSessionFactory().getStatistics()
                            .getSecondLevelCacheHitCount());
            System.out.println("Miss count from second-level session: "
                    + HibernateUtil.getSessionFactory().getStatistics()
                            .getSecondLevelCacheMissCount());
            System.out.println("Put count from second-level session:"
                    + HibernateUtil.getSessionFactory().getStatistics()
                            .getSecondLevelCachePutCount());
            System.out.println();
            System.out.println("Waiting........");
            System.out.println();
            Thread.sleep(5000);
            dbSession2 = HibernateUtil.getSessionFactory().openSession();
            dbTransaction2 = dbSession2.beginTransaction();
            // load the CD from Database
            CD cd3 = (CD) dbSession2.load(CD.class, 4l);
            System.out.println();
            System.out.println("Again Loaded CD from DB - title: "
                    + cd3.getTitle());
            System.out.println();
            dbTransaction2.commit();
            dbSession2.close();
            System.out.println("Fetch count from DB: "
                    + HibernateUtil.getSessionFactory().getStatistics()
                            .getEntityFetchCount());
            System.out.println("Fetch count from second-level session: "
                    + HibernateUtil.getSessionFactory().getStatistics()
                            .getSecondLevelCacheHitCount());
            System.out.println("Miss count from second-level session: "
                    + HibernateUtil.getSessionFactory().getStatistics()
                            .getSecondLevelCacheMissCount());
            System.out.println("Put count from second-level session:"
                    + HibernateUtil.getSessionFactory().getStatistics()
                            .getSecondLevelCachePutCount());
            System.out.println();
            newSession = HibernateUtil.getSessionFactory().openSession();
            newTransaction = newSession.beginTransaction();
            // we have already second-level of cache for the CD
            CD cd4 = (CD) newSession.get(CD.class, 4l);
            System.out.println("CD loaded from Second-level - title: "
                    + cd4.getTitle());
            System.out.println();
            newTransaction.commit();
            newSession.close();
            System.out.println("Fetch count from DB: "
                    + HibernateUtil.getSessionFactory().getStatistics()
                            .getEntityFetchCount());
            System.out.println("Fetch count from second-level session: "
                    + HibernateUtil.getSessionFactory().getStatistics()
                            .getSecondLevelCacheHitCount());
            System.out.println("Miss count from second-level session: "
                    + HibernateUtil.getSessionFactory().getStatistics()
                            .getSecondLevelCacheMissCount());
            System.out.println("Put count from second-level session:"
                    + HibernateUtil.getSessionFactory().getStatistics()
                            .getSecondLevelCachePutCount());
            System.out.println();
        } catch (Exception e) {
            e.printStackTrace();
            if (tran != null && sess.isOpen()) {
                tran.rollback();
            }
            if (dbTransaction1 != null && dbSession1.isOpen()) {
                dbTransaction1.rollback();
            }
            if (dbTransaction2 != null && dbSession2.isOpen()) {
                dbTransaction2.rollback();
            }
            if (newTransaction != null && newSession.isOpen()) {
                newTransaction.rollback();
            }
        } finally {
            HibernateUtil.getSessionFactory().close();
        }
    }
}

 
Now we can configure OSCache in two waysIn Memory and Physical Cache.
In Memory Cache

This is used when we don’t use any oscache.properties file for cache configuration. So in this case the Hibernate gets shut down everytime, memory is flushed and second level cache is built on next run again. Hence you will get the same output again and again while you run the application.

Output
At first run

Fetch count from DB: 0
Fetch count from second-level session: 0
Miss count from second-level session: 0
Put count from second-level session:0
Hibernate:
    select
        cd0_.id as id0_0_,
        cd0_.title as title0_0_,
        cd0_.artist as artist0_0_
    from
        cdcol.cd cd0_
    where
        cd0_.id=?
CD Loaded from DB - title: CD Caching
CD Loaded from First-level session - title: CD Caching
Fetch count from DB: 1
Fetch count from second-level session: 0
Miss count from second-level session: 1
Put count from second-level session:1
Waiting........
Again Loaded CD from DB - title: CD Caching
Fetch count from DB: 1
Fetch count from second-level session: 1
Miss count from second-level session: 1
Put count from second-level session:1
CD loaded from Second-level - title: CD Caching
Fetch count from DB: 1
Fetch count from second-level session: 2
Miss count from second-level session: 1
Put count from second-level session:1

 
At Second run

Fetch count from DB: 0
Fetch count from second-level session: 0
Miss count from second-level session: 0
Put count from second-level session:0
Hibernate:
    select
        cd0_.id as id0_0_,
        cd0_.title as title0_0_,
        cd0_.artist as artist0_0_
    from
        cdcol.cd cd0_
    where
        cd0_.id=?
CD Loaded from DB - title: CD Caching
CD Loaded from First-level session - title: CD Caching
Fetch count from DB: 1
Fetch count from second-level session: 0
Miss count from second-level session: 1
Put count from second-level session:1
Waiting........
Again Loaded CD from DB - title: CD Caching
Fetch count from DB: 1
Fetch count from second-level session: 1
Miss count from second-level session: 1
Put count from second-level session:1
CD loaded from Second-level - title: CD Caching
Fetch count from DB: 1
Fetch count from second-level session: 2
Miss count from second-level session: 1
Put count from second-level session:1

 
Physical Cache

In case of Physical Cache we configure the caching mechanism using oscache.properties file. So if we have a large dataset to cache then Physical Cache comes into the picture and we can save the cache physically into the disk space of the computer. For example we can save into C or D or any other drive in the Windows OS. So in this case we won’t get the same result into the console output. First time only the data are retrieved from the database, second time onwards the data are retrieved from the cache for the same query.

oscache.properties

cache.memory=false
cache.persistence.class=com.opensymphony.oscache.plugins.diskpersistence.DiskPersistenceListener
cache.path=c:/temp/cache
cache.algorithm=com.opensymphony.oscache.base.algorithm.LRUCache

 

In the above file there are few properties which we will see what are these properties

cache.memory : whether cache should be saved into memory, possible values are true/false.
cache.persistence.class : persist the cache data to disk
cache.path : where to store the cache in disk
cache.algorithm : which cache algorithm should be used. For more info http://en.wikipedia.org/wiki/Cache_algorithms

Output
At First run

Fetch count from DB: 0
Fetch count from second-level session: 0
Miss count from second-level session: 0
Put count from second-level session:0
Hibernate:
    select
        cd0_.id as id0_0_,
        cd0_.title as title0_0_,
        cd0_.artist as artist0_0_
    from
        cdcol.cd cd0_
    where
        cd0_.id=?
CD Loaded from DB - title: CD Caching
CD Loaded from First-level session - title: CD Caching
Fetch count from DB: 1
Fetch count from second-level session: 0
Miss count from second-level session: 1
Put count from second-level session:1
Waiting........
Again Loaded CD from DB - title: CD Caching
Fetch count from DB: 1
Fetch count from second-level session: 1
Miss count from second-level session: 1
Put count from second-level session:1
CD loaded from Second-level - title: CD Caching
Fetch count from DB: 1
Fetch count from second-level session: 2
Miss count from second-level session: 1
Put count from second-level session:1

 
At Second run

Fetch count from DB: 0
Fetch count from second-level session: 0
Miss count from second-level session: 0
Put count from second-level session:0
CD Loaded from DB - title: CD Caching
CD Loaded from First-level session - title: CD Caching
Fetch count from DB: 0
Fetch count from second-level session: 1
Miss count from second-level session: 0
Put count from second-level session:0
Waiting........
Again Loaded CD from DB - title: CD Caching
Fetch count from DB: 0
Fetch count from second-level session: 2
Miss count from second-level session: 0
Put count from second-level session:0
CD loaded from Second-level - title: CD Caching
Fetch count from DB: 0
Fetch count from second-level session: 3
Miss count from second-level session: 0
Put count from second-level session:0

 

You can find the generated cache file at C:\temp\cache\application.

For more information on output Fetch count, Miss count or Put count you can refer to the output of EHCache in Hibernate.

If you want to use maven based project then here is the pom.xml file

<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>in.webtuts</groupId>
    <artifactId>mavenoscache</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>
    <name>mavenoscache</name>
    <url>http://maven.apache.org</url>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    <repositories>
        <repository>
            <id>repository.jboss.org-public</id>
            <name>JBoss.org Maven repository</name>
            <url>https://repository.jboss.org/nexus/content/groups/public</url>
        </repository>
    </repositories>
    <dependencies>
        <!-- hibernate -->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-core</artifactId>
            <version>3.6.0.Final</version>
        </dependency>
        <!-- OSCache dependencies -->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-oscache</artifactId>
            <version>3.5.3-Final</version>
        </dependency>
        <dependency>
            <groupId>opensymphony</groupId>
            <artifactId>oscache</artifactId>
            <version>2.4.1</version>
        </dependency>
        <dependency>
            <groupId>javax.jms</groupId>
            <artifactId>jms</artifactId>
            <version>1.1</version>
        </dependency>
        <!-- mysql -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.9</version>
        </dependency>
        <!-- Hibernate library dependecy start -->
        <dependency>
            <groupId>dom4j</groupId>
            <artifactId>dom4j</artifactId>
            <version>1.6.1</version>
        </dependency>
        <dependency>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
            <version>1.1.1</version>
        </dependency>
        <dependency>
            <groupId>commons-collections</groupId>
            <artifactId>commons-collections</artifactId>
            <version>3.2.1</version>
        </dependency>
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>2.2</version>
        </dependency>
        <!-- Hibernate library dependecy end -->
        <dependency>
            <groupId>javax.transaction</groupId>
            <artifactId>jta</artifactId>
            <version>1.1</version>
        </dependency>
        <dependency>
            <groupId>javassist</groupId>
            <artifactId>javassist</artifactId>
            <version>3.12.1.GA</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>3.8.1</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>

 
Thanks for your reading. Please do not forget to leave a comment.

0 thoughts on “Second Level OSCache example in Hibernate

Leave a Reply

Your email address will not be published. Required fields are marked *