Introduction to Hibernate Caching

When you think about Hibernate, the first thing that comes to mind is Hibernate’s ORM which enables you to use your OOP structured classes as database entities. Furthermore, Hibernate provides a multilevel caching mechanism, which is the actual topic of this blog post. 

If this blog post was a tutorial, we could name it “How to speed up your application?”

Levels of cache

First level cache

First level cache is a session cache, which comes configured out of the box and can’t be disabled. All SQL statements must go through the first level cache.

When you make multiple SQL UPDATE statements in one session, the first level cache will try to postpone making the updates in the database until you close the session or commit the transaction manually. This way cache reduces the number of SQL statements by a huge amount. Once the session is closed, all the cached data is lost and can’t be reused by other sessions.

For example, after running this chunk of code:

User user = entityManager
     .createQuery("select u from users u where u.username ='johndoe'", User.class)
return user;

Hibernate will combine all the changes made in this session and create only one SQL UPDATE statement when the session is closed, as shown below.

  select as id1_0_,
     user0_.created_at as created_2_0_,
     user0_.updated_at as updated_3_0_, as email4_0_,
     user0_.first_name as first_name5_0_,
     user0_.last_name as last_name6_0_
     users user0_

After the session is closed, the cache is invalidated and cleared.

Second level cache

Since the first level cache is a session cache, its cached data can be used in that session only. If at the same time another client requires the same data, he will need to fetch the data from the database. This is where second level cache comes in handy. Second level Cache is an optional caching mechanism which allows caching objects across sessions and in order to work, some configuration is needed.

Query level cache

Query level cache is a key-value in memory storage which for keys uses SELECT queries and for values it uses identifiers of each row that is returned for the specified SELECT query. The main goal of this cache is not to speed up the retrieval of entities from cache but to speed up the querying time, that is why Hibernate does not cache the actual results of the query, but the indexes of rows which will be returned when the query is run. 

Query level cache is especially helpful when you have some queries which take long to complete and are run often. In order for query level cache to work, queries must be identical. The slightest change of the SELECT query will result in results not found in cache and Hibernate will need to query the database for the results. For query level cache to work, slight configuration is needed.


The first part of configuration is selecting a cache provider. You can choose the provider which suits your project the best from the list of six providers which Hibernate supports. We are going to use EhCache, since it meets the expectations of most applications.

To configure the provider, you should add the following line to your persistence.xml.

<property name="hibernate.cache.provider_class">

After choosing the provider, we are only one step away from having a fully functional Hibernate query cache implemented.

The last step is adding annotations to our entity to make it cacheable. When adding annotations to the entity, we have to specify which concurrency strategy we are going to use. Choosing the concurrency strategy depends a lot from the type of entity you are caching. Is the data going to be changed a lot and is it going to be a problem if sometimes you get stale data from the cache? If stale data is not going to be a problem, you should go with the NONSTRICT_READ_WRITE strategy, and if you can’t tolerate stale data, the best choice for you is the READ_WRITE strategy, which guarantees consistency.

Annotations are added to the entity class definition, like this:

package models.entities;

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

import javax.persistence.Cacheable;
import javax.persistence.Entity;

@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class Employee extends BaseModel {
   private String username;
   private String email;
   private String firstName;
   private String lastName;

And just like that, your cache is ready. 

May your queries be quick and your data safe. Good luck!

“Hibernate Caching” Tech Bite was brought to you by Asmir Ljumić, Software Engineer at Atlantbh.

Tech Bites are tips, tricks, snippets or explanations about various programming technologies and paradigms, which can help engineers with their everyday job.

Software DevelopmentTech Bites
February 23, 2024

Background Jobs in Elixir – Oban

When and why do we need background jobs? Nowadays, background job processing is indispensable in the world of web development. The need for background jobs stems from the fact that synchronous execution of time-consuming and resource-intensive tasks would heavily impact an application's  performance and user experience.  Even though Elixir is…
QA/Test AutomationTech Bites
December 22, 2023

Selenium Grid 4 with Docker

Introduction When talking about automation testing, one of the first things that comes to mind is Selenium. Selenium is a free, open-source automated testing framework used to validate web applications across different browsers and platforms. It is not just a single tool but a suite of software. Every component of…

Want to discuss this in relation to your project? Get in touch:

Leave a Reply