When working with large-scale enterprise applications, database access is often the primary performance bottleneck. Hibernate, as an ORM (Object Relational Mapping) framework, provides several caching layers to minimize expensive database calls.
One of the most powerful features is the Second-Level Cache, which persists entities beyond the scope of a single Session
. Unlike the First-Level Cache (which is session-specific), the second-level cache is associated with the SessionFactory
and shared across sessions.
Think of it like saving answers in a notebook instead of asking the teacher again every time. This significantly improves performance by reducing redundant queries.
What is Second-Level Cache in Hibernate?
- First-Level Cache (Session Cache): Default, mandatory, and exists only per session.
- Second-Level Cache (SessionFactory Cache): Optional, configurable, and shared across sessions.
- Query Cache: Works alongside second-level cache to cache query result sets.
Second-Level Cache helps in:
- Reducing database round-trips.
- Improving read-heavy application performance.
- Making Hibernate applications more scalable.
Configuring Second-Level Cache in Hibernate
1. Enable Second-Level Cache in hibernate.cfg.xml
<hibernate-configuration>
<session-factory>
<!-- Database connection properties -->
<property name="hibernate.dialect">org.hibernate.dialect.MySQL8Dialect</property>
<!-- Enable second-level cache -->
<property name="hibernate.cache.use_second_level_cache">true</property>
<property name="hibernate.cache.use_query_cache">true</property>
<!-- Specify cache provider (Ehcache) -->
<property name="hibernate.cache.region.factory_class">
org.hibernate.cache.ehcache.EhCacheRegionFactory
</property>
</session-factory>
</hibernate-configuration>
2. Add Dependencies (Maven Example)
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-ehcache</artifactId>
<version>5.6.15.Final</version>
</dependency>
<dependency>
<groupId>org.infinispan</groupId>
<artifactId>infinispan-hibernate-cache</artifactId>
<version>13.0.0.Final</version>
</dependency>
Entity Configuration with Cache
You must annotate entities to mark them as cacheable.
import jakarta.persistence.*;
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
@Entity
@Table(name = "users")
@Cacheable
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String email;
// Getters and Setters
}
Cache Strategies
- READ_ONLY: Best for reference data that never changes.
- NONSTRICT_READ_WRITE: Allows occasional stale data.
- READ_WRITE: Guarantees strong consistency.
- TRANSACTIONAL: For JTA-enabled transactional caches (e.g., Infinispan).
Example: Second-Level Cache in Action
Step 1: Insert Data
SessionFactory factory = new Configuration().configure().buildSessionFactory();
Session session1 = factory.openSession();
Transaction tx1 = session1.beginTransaction();
User user = new User();
user.setName("Alice");
user.setEmail("alice@example.com");
session1.persist(user);
tx1.commit();
session1.close();
Step 2: Fetch from Database (First Query Hits DB)
Session session2 = factory.openSession();
User u1 = session2.get(User.class, 1L);
System.out.println("Fetched User: " + u1.getName());
session2.close();
Step 3: Fetch from Cache (Second Query Uses Cache)
Session session3 = factory.openSession();
User u2 = session3.get(User.class, 1L);
System.out.println("Fetched from Cache: " + u2.getName());
session3.close();
✅ The second fetch won’t hit the database—Hibernate retrieves it from the second-level cache.
Query Cache Example
List<User> users = session.createQuery("from User", User.class)
.setCacheable(true)
.list();
⚠️ Query Cache must be explicitly enabled with .setCacheable(true)
.
Performance Considerations
- Use cache only for frequently accessed, rarely updated data.
- Avoid caching volatile data (e.g., stock prices, live metrics).
- Choose the right concurrency strategy.
- Monitor cache hit/miss ratios.
Common Pitfalls (Anti-Patterns)
- Misusing Eager Fetching: Leads to cache overloading.
- Caching Write-Heavy Entities: Causes stale data and synchronization issues.
- Forgetting Transaction Isolation: Leads to inconsistent data.
Integration with Spring Boot
In Spring Boot, you just need to enable cache in application.properties
:
spring.jpa.properties.hibernate.cache.use_second_level_cache=true
spring.jpa.properties.hibernate.cache.use_query_cache=true
spring.jpa.properties.hibernate.cache.region.factory_class=org.hibernate.cache.ehcache.EhCacheRegionFactory
📌 Hibernate Version Notes
-
Hibernate 5.x:
SessionFactory
with XML-based configs.- Ehcache commonly used as provider.
-
Hibernate 6.x:
- Jakarta Persistence namespace (
jakarta.persistence
). - Enhanced SQL support and query cache improvements.
- Infinispan better integrated as provider.
- Jakarta Persistence namespace (
Best Practices
- Use READ_ONLY for static data like countries, roles, categories.
- Combine First-Level + Second-Level + Query Cache for maximum efficiency.
- Regularly clear and monitor cache metrics.
- Prefer Ehcache for simple apps, Infinispan for distributed apps.
Conclusion and Key Takeaways
- Second-Level Cache boosts performance by sharing entities across sessions.
- Hibernate supports multiple providers like Ehcache, Infinispan, OSCache.
- Use the right caching strategy depending on entity volatility.
- Avoid common pitfalls like caching write-heavy entities.
- Always test performance with realistic workloads.
FAQ
1. What’s the difference between Hibernate First-Level and Second-Level Cache?
First-Level is session-specific, Second-Level is session-factory-wide and shared.
2. Can I disable Second-Level Cache for certain entities?
Yes, just don’t annotate them with @Cacheable
.
3. Which is better: Ehcache or Infinispan?
Ehcache is simpler for standalone apps; Infinispan is distributed and scalable.
4. How does Hibernate Query Cache work?
It stores query result sets but requires Second-Level Cache for entities.
5. What happens when cached data becomes stale?
Depends on the concurrency strategy: READ_WRITE ensures synchronization, READ_ONLY does not.
6. Is Hibernate caching similar to database caching?
Not exactly—Hibernate caches entities/queries, while DB caches queries/indexes.
7. How to monitor Hibernate cache usage?
Enable statistics: hibernate.generate_statistics=true
and use JMX tools.
8. Can I use Redis with Hibernate Second-Level Cache?
Yes, via third-party plugins like Hibernate-Redis integration.
9. Is caching always recommended?
No, use it wisely for read-heavy, less volatile entities.
10. What’s new in Hibernate 6 caching?
Improved support for distributed caches, Jakarta namespace, and optimized query plans.