hibernate

Hibernate First Level Cache Example

In this post we will talk about the Hibernate First Level Cache Strategy. When an application repeatedly hits the database and executes a lot of queries, it’s important as developer to apply a cache between then. Caching recently accessed objects is a very simple way to improve the performance, avoiding having to access the database each time it is required to get an object.

1. What is the First Level Cache in Hibernate?

Hibernate as an ORM Tool provides three caching strategies (First Level, Second Level, Query) from which the most basic and simplest is the first level cache. How we activate this cache? Simple, it’s A-U-T-O-M-A-T-I-C ! Let’s look at the features:

  • First level Cache is the transactional cache associated with the Session Object. A  Session object, is the basic way to perform operations in a database, it can be compared to a JDBC Connection.
  • It’s enabled by default.
  • The users can’t disable this level.
  • The cache is available during the lifespan of the Session Object.
  • If the session is closed, cached objects be lost.
  • Cached objects aren’t shared between sessions.

And that’s all we need to know for implementing the First Level Cache of Hibernate in a application.

2. What we need ?

You can choose the tools that you most like, but that provides you a Hibernate Wizard to create the mapping files (.hbm.xml), because makes a xml file manually can be tedious. For this example we use this set of tools:

  • Eclipse Kepler (4.3) SR2
  • JBoss Tools Plugin (Kepler Version). Search them in the Eclipse Marketplace
  • Java JDK 1.7_67
  • Hibernate 4.5.2 final (You can download it here)
  • JDBC Driver: MySQL Connector 5.1.34
  • MySQL 5.6 Community Server

3. How to configure Hibernate?

Please follow these steps:

  1. After to download and configure the JBoss Plugin in Eclipse Kepler, we create a Java Project with a JDK 1.7 runtime
  2. Then, we need to reference the required Libraries. Make right click in the project and navegate to Build Path > Add External Archives, please we must associate the MySQL Driver or your Database Driver, and from Hibernate library all the jars included in this paths “/hibernate/dist/lib/required” and “/hibernate/dist/lib/provided”; all this to avoid ClassNotFoundException.
  3. Our project must look like this:

First Level Cache Hibernate Example
First Level Cache Hibernate Example

4. The Example

Let’s Get It Started!

Open a hibernate wizard and create a hibernate configuration file.

hibernate.cfg.xml

<?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>
		<!-- Database Connection properties -->
		<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
		<property name="connection.url">jdbc:mysql://localhost:3307/test</property>
		<property name="connection.username">admin</property>
		<property name="connection.password">admin</property>

		<!-- hibernate dialect -->
		<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>

		<!-- Show the executed queries -->
		<property name="show_sql">true</property>

		<!-- Mapped POJO's -->
		<mapping resource="com/javacodegeeks/hibernate/cache/Department.hbm.xml" />

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

In this file, we set all the parameters for customize the hibernate framework, and how it connects to the database. It’s important that we create this configuration file in the root of the project, inside no package. Remember to mantain session-factory element without name attribute if you don’t want to registry it on JNDI Context.

After, we need to define the mapping files of each entity, for this we use the Hibernate Wizard > Create XML Mapping File.

Department.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">
<!-- Generated 11/02/2015 11:03:03 PM by Hibernate Tools 3.6.0 -->
<hibernate-mapping>
    <class name="com.javacodegeeks.hibernate.cache.Department" table="department" catalog="test">
        <id name="idDepartment" type="int">
            <column name="idDepartment" />
            <generator class="assigned" />
        </id>
        <property name="name" type="string">
            <column name="name" length="6" not-null="true" />
        </property>
    </class>
</hibernate-mapping>

By default, this file must be defined in the same package of the class, but we can set in any place, changing the mapping resource path in hibernate.cfg.xml.

HibernateSessionFactory.java

package com.javacodegeeks.hibernate.cache;

import org.hibernate.HibernateException;
import org.hibernate.SessionFactory;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;

/**
 * @author Andres.Cespedes
 * @version 1.0 $Date: 11/02/2015
 * @since 1.7
 * 
 */
public class HibernateSessionFactory {

	private static SessionFactory sessionFactory;

	// Static block for initialization
	static {
		try {
			// Create the Configuration object from hibernate.cfg.xml
			Configuration configuration = new Configuration().configure();
			// New way to create a Session Factory in Hibernate4
			StandardServiceRegistryBuilder serviceRegistryBuilder = new StandardServiceRegistryBuilder();
			// Enforces to set the configuration
			serviceRegistryBuilder.applySettings(configuration.getProperties());
			ServiceRegistry serviceRegistry = serviceRegistryBuilder.build();
			// with the serviceRegistry creates a new Factory, and sets
			setSessionFactory(configuration
					.buildSessionFactory(serviceRegistry));
		} catch (HibernateException he) {
			System.err
					.println("There was an error while creating the SessionFactory: "
							+ he.getMessage());
		}
	}

	/**
	 * @return the sessionFactory
	 */
	public static SessionFactory getSessionFactory() {
		return sessionFactory;
	}

	/**
	 * @param sessionFactory
	 *            the sessionFactory to set
	 */
	public static void setSessionFactory(SessionFactory sessionFactory) {
		HibernateSessionFactory.sessionFactory = sessionFactory;
	}

}

This is a util class to factory the SessionFactory object. In Hibernate 4 for set configuration is necessary to set a ServiceRegistry as a parameter for to invoke the method buildSessionFactory.

Department.java

package com.javacodegeeks.hibernate.cache;

import java.io.Serializable;

/**
 * @author Andres.Cespedes
 * @version 1.0 $Date: 11/02/2015
 * @since 1.7
 * 
 */
public class Department implements Serializable {

	private static final long serialVersionUID = 1997660946109705991L;
	private int idDepartment;
	private String name;

	public Department() {
	}

	public Department(int idDepartment, String name) {
		this.idDepartment = idDepartment;
		this.name = name;
	}

	/**
	 * @return the idDepartment
	 */
	public int getIdDepartment() {
		return idDepartment;
	}

	/**
	 * @param idDepartment
	 *            the idDepartment to set
	 */
	public void setIdDepartment(int idDepartment) {
		this.idDepartment = idDepartment;
	}

	/**
	 * @return the name
	 */
	public String getName() {
		return name;
	}

	/**
	 * @param name
	 *            the name to set
	 */
	public void setName(String name) {
		this.name = name;
	}

}

This is the POJO File, or DTO or Java Bean, or whatever you want, but it’s so important that the properties match with the definition in mapping file.

HibernateFirstLevelCacheMain.java

package com.javacodegeeks.hibernate.cache;

import org.hibernate.Session;
import org.hibernate.SessionFactory;

/**
 * @author Andres.Cespedes
 * @version 1.0 $Date: 11/02/2015
 * @since 1.7
 * 
 */
public class HibernateFirstLevelCacheMain {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		final int ID_OBJECT = 2015;
		// 1. Get one single instance of sessionFactory
		SessionFactory sessionFactory = HibernateSessionFactory
				.getSessionFactory();
		// 2. Open a session to do operations
		Session session = sessionFactory.openSession();
		// 3. Save an object in database.
		session.beginTransaction();
		session.save(new Department(ID_OBJECT, "Malaga"));
		session.getTransaction().commit();
		// 4. Load the previous object from 'database', really from session
		Department loaded = (Department) session.get(Department.class,
				ID_OBJECT);
		System.out.println("The Department name is: " + loaded.getName());
		// 5. Change the name to compare the object with the database entity
		loaded.setName("Madrid");
		// 6. Load again the object
		loaded = (Department) session.get(Department.class, ID_OBJECT);
		System.out.println("The Department name is: " + loaded.getName());
		// 7. Return the connection, closing the hibernate's session
		session.close();

		// 8. Open another session to do operations
		session = sessionFactory.openSession();
		// 9. Get the name to compare the Session object, after close the last
		// session
		loaded = (Department) session.get(Department.class, ID_OBJECT);
		System.out.println("The Department name is: " + loaded.getName());

		System.exit(0);
	}
}

What is happening here?

  • After we get the Session, we save a new department in the database. In this 3rd step, hibernate writes in the table but it also ‘writes’ the object in the session cache. With this we don’t need to access to the database in any load option.
  • In the 4th and 6th steps, Hibernate gets the object from the cache with a really faster response. How can we verify these? We can et to to true, the show_sql parameter in the configuration file. In that case, if no query is printed in stdout, Hibernate doesn’t “travel” to MySQL.
  • In the 7th step the session is closed, and the cache is flushed, so, if we get the object again (9th step), then Hibernate accesses the database.

The Output will be:

Hibernate: insert into test.department (name, idDepartment) values (?, ?)
The Department name is: Malaga
The Department name is: Madrid
Hibernate: select department0_.idDepartment as idDepart1_0_0_, department0_.name as name2_0_0_ from test.department department0_ where department0_.idDepartment=?
The Department name is: Malaga

Tips

  • The configuration file must be at the root.
  • Column and attribute names are case-sensitive, so, you must name them carefully.
  • Make sure that the session-factory element in hibernate configuration has no name attribute.
  • Cache is enabled by default, and cannot be deactivated.
  • Cache is only in the Session object.
  • Hibernate is caching all objects at first level, so if you want to work with queries that load a large amount of objects, it’s necessary to clear the cache to prevent memory pitfalls.
  • It’s so important that you know that as many caches, the First Level can contains old values, so, you need to develop a strategy to avoid this issue.

5. Download the Eclipse Project

Download
You can download the full source code of this example here: HibernateFirstLevelCacheExample

Andres Cespedes

Andres is a Java Software Craftsman from Medellin Colombia, who strongly develops on DevOps practices, RESTful Web Services, Continuous integration and delivery. Andres is working to improve software process and modernizing software culture on Colombia.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button