Hibernate Caching Feature and How to Use It

Posted on the 14 March 2013 by Skiabox @skiabox

Hibernate is a really cool ORM tool that can be used from a Java project , as an intermediate layer between our project and the underlying database.

In this example I will use MySql as the underlying database, although any database can be used to support our application.

The IDE that I will use is IntelliJ IDEA 12.0.4 Ultimate Edition and the build tool I will use is Maven.

1. Open IntelliJ Idea
2. Choose File -> New Project
3. Make the following selections :

4.Then we select the following archetype (I am using Maven 3.0.5):

5.We accept the defaults of the next step by pressing 'Finish' button.

Next Maven is downloading the appropriate files and we may see a message from IntelliJ Idea about 'Maven projects need to be imported'.
Here we press 'Enable Auto-Import', so that IntelliJ Idea can import maven projects automatically from now on.

6.We create a resources folder inside /src/main/, which is maven's default folder for saving resource files (e.g. configuration files):

Now let's create our classes.
We will create a Person class with a userId and a userName as its private instance variables along with the corresponding getters and setters.
Here is the code :

package org.skiabox.dto;

public class Person {

    private int userId;
    private String userName;

    //getters and setters
    public int getUserId() {
        return userId;
    }

    public void setUserId(int userId) {
        this.userId = userId;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }
}

...and the new project hierarchy

Now we must declare the Person class as an entity.
So we add the @Entity annotation above the class (this is a JPA 2.0 annotation so when IntelliJ Idea asks as what dependency to import into the maven project we search using javax.peristence -which is the package that the Entity class belong to- and IntelliJ Idea gives us the choice to add the hibernate implementation of JPA 2.0)

Next add the following hibernate.cfg.xml file in the resources folder we created earlier :

<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
        "-//Hibernate/Hibernate Configuration DTD//EN"
        "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
    <session-factory>
        <property name="connection.url">jdbc:mysql://localhost:3306/hibernatedb</property>
        <property name="connection.driver_class">com.mysql.jdbc.Driver</property>
        <property name="connection.username">username</property>
        <property name="connection.password">password</property>

        <!-- DB schema will be created if needed -->
        <property name="hbm2ddl.auto">create</property>


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

        <!-- JDBC connection pool (use the built-in) -->
        <property name="connection.pool_size">1</property>
        <!-- SQL dialect -->
        <property name="dialect">org.hibernate.dialect.MySQLDialect</property>
        <!-- Echo all executed SQL to stdout -->
        <property name="show_sql">true</property>

        <!-- Names the annotated entity class -->

    </session-factory>
</hibernate-configuration>

Note here that at this point we set hbm2dll.auto property to 'create' because we want to create 10 person objects, so that we can fill our database with data.
Also note the enable second level cache part, which is the proper implementation for hibernate 4+ versions.
Now IntelliJ IDea will complain about Sql Dialect since it understand that we want to use Hibernate at this project, so let it add at this stage a Hibernate Facet(it just adds some Hibernate libraries).
It also complaints about mysql jdbc driver at the second property line.
Here's where maven comes to a rescue.
Since IntelliJ does not offer to add a dependency for this line we'll do it manually.
Go at this page : http://mvnrepository.com/

This is an mvn repository search engine and we will use it to search for us for 'mysql jdbc driver'.
Then we click the first link (MySql Java Connector) and there we can find the maven dependency which we can add it into our pom.xml file, which should look like this right now :

<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>Maven_Hibernate_Project</groupId>
  <artifactId>Maven_Hibernate_Project</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>jar</packaging>

  <name>Maven_Hibernate_Project</name>
  <url>http://maven.apache.org</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
      <dependency>
          <groupId>org.hibernate.javax.persistence</groupId>
          <artifactId>hibernate-jpa-2.0-api</artifactId>
          <version>1.0.1.Final</version>
      </dependency>
      <dependency>
          <groupId>mysql</groupId>
          <artifactId>mysql-connector-java</artifactId>
          <version>5.1.23</version>
      </dependency>

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

      <dependency>
          <groupId>org.hibernate</groupId>
          <artifactId>hibernate-ehcache</artifactId>
          <version>4.1.10.Final</version>
      </dependency>
  </dependencies>
</project>

The other two dependencies are the hibernate-core and the ehcache implementation packaged by hibernate team.
Especially the second dependency is vital , since it is not included in IntelliJ Idea hibernate facet.

Ok now we edit the App.java file which will be the starting point of our application.
The code you see is the most updated method for creating a session, since some older methodologies are deprecated (even IntelliJ Idea 12.0.4 uses a deprecated method in its Hibernate starting method, but I posted a newer solution which I believe they'll include in their future ide versions).

package Maven_Hibernate_Project;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.ServiceRegistryBuilder;

/**
 * Hello world!
 *
 */
public class App
{
    private static SessionFactory sessionFactory;
    private static ServiceRegistry serviceRegistry;

    public static void main( String[] args )
    {

        Configuration configuration = new Configuration();
        configuration.configure();

        serviceRegistry = new ServiceRegistryBuilder().applySettings(configuration.getProperties()).buildServiceRegistry();
        sessionFactory = configuration.buildSessionFactory(serviceRegistry);

        //Complete session cycle
        Session session = sessionFactory.openSession();
        session.beginTransaction();

        //session code

        session.getTransaction().commit();
        session.close();
    }
}

Don't forget to add @Id annotation along with @GeneratedValue (strategy = GenerationType.AUTO) annotation over userId property, so that the underlying database automatically create the appropriate key.
So now the Person.java code becomes :

package org.skiabox.dto;


import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
public class Person {

    @Id @GeneratedValue(strategy = GenerationType.AUTO)
    private int userId;
    private String userName;

    //getters and setters
    public int getUserId() {
        return userId;
    }

    public void setUserId(int userId) {
        this.userId = userId;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }
}

A note here is that IntelliJ Idea adds the new method imports using the local hibernate library and not by adding new maven dependencies inside pom.xml

Now to create ten persons in our database we use the following code inside our App.java main method :

package Maven_Hibernate_Project;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.ServiceRegistryBuilder;
import org.skiabox.dto.Person;


public class App
{
    private static SessionFactory sessionFactory;
    private static ServiceRegistry serviceRegistry;

    public static void main( String[] args )
    {

        Configuration configuration = new Configuration();
        configuration.configure();

        serviceRegistry = new ServiceRegistryBuilder().applySettings(configuration.getProperties()).buildServiceRegistry();
        sessionFactory = configuration.buildSessionFactory(serviceRegistry);

        //Complete session cycle
        Session session = sessionFactory.openSession();
        session.beginTransaction();

        //session code
        for (int counter = 1; counter < 11; counter ++)
        {
            Person person = new Person();
            person.setUserName("Person " + counter);
            session.save(person);
        }

        session.getTransaction().commit();
        session.close();
    }
}

After executing this code , wee see all the inserts executed at our console because we have set property 'show_sql' to true inside our hibernate.cfg.xml
Our console will show this :

We can also check the database with our favorite tool or even better from inside IntelliJ Idea to see the table contents.
Here is an image of the table from inside IntelliJ Idea :

Now let's try to get a user's data first without using cache.
Don't forget at this step to modify 'hbm2ddl.auto' property inside hibernate.cfg.xml from create to update, otherwise our table will be deleted and created again every time we access it and we will loose our saved records.
So we modify our App.java to look like this (using for example user 5):

package Maven_Hibernate_Project;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.ServiceRegistryBuilder;
import org.skiabox.dto.Person;

public class App
{
    private static SessionFactory sessionFactory;
    private static ServiceRegistry serviceRegistry;

    public static void main( String[] args )
    {

        Configuration configuration = new Configuration();
        configuration.configure();

        serviceRegistry = new ServiceRegistryBuilder().applySettings(configuration.getProperties()).buildServiceRegistry();
        sessionFactory = configuration.buildSessionFactory(serviceRegistry);

        //---Session 1
        Session session1 = sessionFactory.openSession();
        session1.beginTransaction();

        //session code
        Person person = (Person)session1.get(Person.class, 5);

        session1.getTransaction().commit();
        session1.close();

        //---Session 2
        Session session2 = sessionFactory.openSession();
        session2.beginTransaction();

        //session code
        Person person2 = (Person)session2.get(Person.class, 5);

        session2.getTransaction().commit();
        session2.close();
    }
}

If we check the console we see two select commands, although the data remains the same like this :

Now here is where cache comes into play.
We modify our entity class like (Person.java) this :

package org.skiabox.dto;


import org.hibernate.annotations.*;
import org.hibernate.annotations.Cache;
import javax.persistence.*;
import javax.persistence.Entity;

@Entity
@Cacheable
@Cache(usage = CacheConcurrencyStrategy.READ_ONLY)
public class Person {

    @Id @GeneratedValue(strategy = GenerationType.AUTO)
    private int userId;
    private String userName;

    //getters and setters
    public int getUserId() {
        return userId;
    }

    public void setUserId(int userId) {
        this.userId = userId;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }
}

In our case we only read data from the database so we define our Cache Concurrency Strategy as read only.
Of course there are other strategies to try and you can find more details here.

Finally we run again our app, and we realize that our app did only one query to the database this time :