Date: March 4, 2025
Caching is a cornerstone technique to optimize database interactions and application performance in Hibernate-based Java applications. Hibernate provides two main caching layers:
- First-Level Cache (Session Cache)
- Second-Level Cache (Shared Cache)
Understanding how they work and when to use them is key to writing efficient data access code.
What is the First-Level Cache?
The First-Level Cache is associated with the Hibernate Session
object. It is:
- Mandatory and always enabled
- Scoped to a single session/transaction
- Stores entity instances retrieved or saved during that session
- Automatically cleared when the session is closed or cleared
How it works:
When you query an entity by its identifier within the same session, Hibernate first looks in the first-level cache before hitting the database.
Example:
Session session = sessionFactory.openSession();
session.beginTransaction();
User user1 = session.get(User.class, 1L); // Hits database and caches user with ID 1
User user2 = session.get(User.class, 1L); // Retrieves user from first-level cache, no DB hit
session.getTransaction().commit();
session.close();
In the above code, the second session.get()
does not execute a SQL query, it retrieves the cached entity from the session cache.
What is the Second-Level Cache?
The Second-Level Cache is optional and can be configured to cache entities across sessions, meaning it is:
- Shared across all sessions in the same SessionFactory
- Useful for read-heavy applications
- Typically backed by caching providers like Ehcache, Infinispan, Redis, etc.
- Must be explicitly enabled and configured
Why use it?
Imagine many users querying the same data concurrently. Without a second-level cache, each session hits the database independently. The second-level cache reduces this redundant DB load.
Enabling Second-Level Cache in Hibernate
Step 1: Add caching provider dependencies
For example, add Ehcache in your pom.xml
:
<dependency>
<groupId>org.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>3.10.0</version>
</dependency>
Step 2: Enable caching in application.properties
spring.jpa.properties.hibernate.cache.use_second_level_cache=true
spring.jpa.properties.hibernate.cache.region.factory_class=org.hibernate.cache.jcache.JCacheRegionFactory
spring.cache.jcache.config=classpath:ehcache.xml
Step 3: Annotate your entity with caching strategy
@Entity
@Cacheable
@org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class User {
@Id
private Long id;
private String email;
// getters and setters
}
Step 4: Provide ehcache.xml
configuration
<ehcache:config xmlns:ehcache="http://www.ehcache.org/v3">
<ehcache:cache alias="com.example.User">
<ehcache:heap unit="entries">1000</ehcache:heap>
<ehcache:expiry>
<ehcache:ttl unit="minutes">10</ehcache:ttl>
</ehcache:expiry>
</ehcache:cache>
</ehcache:config>
Cache Regions and Concurrency Strategies
Hibernate caching supports different concurrency strategies:
- READ_ONLY: for data that never changes
- READ_WRITE: supports concurrent access with locking
- NONSTRICT_READ_WRITE: less strict locking, suitable for rarely updated data
- TRANSACTIONAL: requires a transactional cache provider
Choosing the right strategy depends on your use case.
Summary
Feature | First-Level Cache | Second-Level Cache |
---|---|---|
Scope | Single Hibernate Session | Shared across sessions (SessionFactory) |
Enabled by default | Yes | No (must be enabled) |
Use case | Session-scoped caching | Cross-session, shared caching |
Provider required | No | Yes (Ehcache, Infinispan, Redis, etc.) |
Configuration complexity | Minimal | Moderate |
Understanding and leveraging Hibernate’s caching layers effectively can drastically improve your application’s database performance, especially for frequently accessed or read-only data.
Next up: Mastering Hibernate Caching – Part 2: Integrating Ehcache and Redis with Hibernate