Learnitweb

An introduction to caching in Hibernate

1. Introduction

Accessing a database is an expensive operation even if it is a simple query. The request is sent to the server over the network. The database server then compiles the SQL into a query plan. The query plan is executed and returned back to the client over the network. This complete flow may affect the performance of the application. Some databases cache the result which avoid repeated disk I/O but still the call is made to the server. That is why the better approach is to have a cache at the client side.

JDBC itself does not provide any such feature but Hibernate does. Hibernate provides two levels of cache:

  1. First-level cache (or L1 cache): The persistent context (EntityManager or Session) is also called the first-level cache. First-level cache is enabled by default. All requests are passed through first-level or L1 cache. A managed entity is added to the internal cache of the persistent context.
    The first-level cache is always checked for the requested object before checking the second-level cache. The first-level cache ensures that within a session, whenever an object is requested, the same object instance is returned. This avoids the execution of multiple queries when the same object is requested multiple times. The Hibernate Session acts as a transaction-level cache of persistent data.
  2. Second-level cache (or L2 cache): The second-level cache is optional and configurable. Second-level cache is not part of the Hibernate and is configurable. There is nothing specific required to access object from the second-level cache using the session. The access of second-level cache is transparent to the user. The benefit of second-level cache is that it can be shared between applications on the same or different machines.

2. Hibernate Session and Caches

The following diagram shows the interaction between Session and two caches.

Hibernate session and caches

3. Example of First-level cache

Following is a typical example of accessing data from database. Here, Employee is the entity managed by the Hibernate.

import org.hibernate.Session;
import org.hibernate.Transaction;

import com.learnitweb.entity.Employee;
import com.learnitweb.util.HibernateUtil;

public class Client {

	public static void main(String[] args) {
		Transaction transaction = null;
		try (Session session = HibernateUtil.getSessionFactory().openSession()) {

			transaction = session.beginTransaction();

			Employee e1 = session.get(Employee.class, 1L);
			Employee e2 = session.get(Employee.class, 1L);
			Employee e3 = session.get(Employee.class, 1L);
			Employee e4 = session.get(Employee.class, 1L);

			System.out.println(e1.getName());
			System.out.println(e2.getName());
			System.out.println(e3.getName());
			System.out.println(e4.getName());

			transaction.commit();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

Employee.java

@Entity
public class Employee {
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	@Column(name = "id")
	private Long id;

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

//getter and setter removed for brevity
	
}

Note that we are trying to get the Employee record having Id as 1. We are trying to access the same record 4 times in the same session. If you observe the logs you will observe that the query is executed only once not once.

Hibernate: select employee0_.id as id1_0_0_, employee0_.name as name2_0_0_ from Employee employee0_ where employee0_.id=?

Now lets try to access the record again in a new session.

import org.hibernate.Session;
import org.hibernate.Transaction;

import com.learnitweb.entity.Employee;
import com.learnitweb.util.HibernateUtil;

public class Client {

	public static void main(String[] args) {
		Transaction transaction = null;
		try (Session session = HibernateUtil.getSessionFactory().openSession()) {

			transaction = session.beginTransaction();

			Employee e1 = session.get(Employee.class, 1L);
			Employee e2 = session.get(Employee.class, 1L);
			Employee e3 = session.get(Employee.class, 1L);
			Employee e4 = session.get(Employee.class, 1L);

			System.out.println(e1.getName());
			System.out.println(e2.getName());
			System.out.println(e3.getName());
			System.out.println(e4.getName());

			transaction.commit();
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		try (Session session = HibernateUtil.getSessionFactory().openSession()) {

			transaction = session.beginTransaction();

			Employee e1 = session.get(Employee.class, 1L);
			Employee e2 = session.get(Employee.class, 1L);
			Employee e3 = session.get(Employee.class, 1L);
			Employee e4 = session.get(Employee.class, 1L);

			System.out.println(e1.getName());
			System.out.println(e2.getName());
			System.out.println(e3.getName());
			System.out.println(e4.getName());

			transaction.commit();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

You can observe that the following queries executed in the logs, one query for each session. Since we are trying to get the same object 4 times in a session, only two queries are executed.

Hibernate: select employee0_.id as id1_0_0_, employee0_.name as name2_0_0_ from Employee employee0_ where employee0_.id=?
Hibernate: select employee0_.id as id1_0_0_, employee0_.name as name2_0_0_ from Employee employee0_ where employee0_.id=?

4. Conclusion

In this tutorial, we discussed Hibernate cache and two levels of cache. We discussed first-level cache with an example. We’ll discuss the second-level cache in next tutorial.