Skip to content
Home Java Hibernate Caching — First and Second Level

Hibernate Caching — First and Second Level

Where developers are forged. · Structured learning · Free forever.
📍 Part of: Hibernate & JPA → Topic 6 of 7
Enhance application performance by mastering Hibernate's caching layers.
⚙️ Intermediate — basic Java knowledge assumed
In this tutorial, you'll learn
Enhance application performance by mastering Hibernate's caching layers.
  • L1 cache is the Session-level buffer that ensures repeatable reads and identity preservation within a single transaction.
  • L2 cache is the cross-session store that drastically reduces database load for common entities across the entire application lifecycle.
  • The Query Cache does not store entities; it stores the IDs of entities matching your query. You must have Entity Caching enabled for Query Caching to be truly efficient.
✦ Plain-English analogy ✦ Real code with output ✦ Interview questions
Quick Answer

Think of Hibernate Caching — First and Second Level as a two-tier storage system in a library. The First Level Cache is like the desk where you're currently working; any book you've already opened is right there for instant access. The Second Level Cache is like a shared cart in the hallway that multiple librarians can use; if you don't have the book on your desk, you check the cart before making the long trip back to the main archives (the database).

Hibernate Caching — First and Second Level is a fundamental concept in Java persistence development. It serves as a mechanism to minimize expensive database hits by keeping frequently accessed entities in memory. Understanding the distinction between these layers is critical for building high-performance, scalable Spring Boot applications.

In this guide, we'll break down exactly what Hibernate Caching is, why the two-level architecture was designed to balance isolation and shared access, and how to configure them correctly in production-grade environments. We will explore how the Persistence Context manages state and how external providers like Ehcache 3 or Hazelcast plug into the Hibernate lifecycle to provide distributed caching capabilities.

By the end, you'll have both the conceptual understanding and practical code examples to implement Hibernate caching strategies with confidence, ensuring your 'io.thecodeforge' microservices handle high-traffic loads with minimal latency.

What Is Hibernate Caching — First and Second Level and Why Does It Exist?

The First Level Cache (L1) is mandatory and associated with the Session object. It ensures that within a single transaction, the same entity is not fetched from the database multiple times. This is also known as the 'Persistence Context' or 'Identity Map' pattern.

The Second Level Cache (L2) is optional and exists at the SessionFactory level, shared across all sessions. It solves the problem of cross-session data redundancy and reduces the load on the database for read-heavy applications. While L1 is short-lived (lasting only as long as the transaction), L2 is long-lived and typically managed by a third-party provider. This architecture allows Hibernate to balance data consistency (L1) with global performance optimization (L2).

io/thecodeforge/config/HibernateCacheConfig.java · JAVA
1234567891011121314151617181920212223242526272829303132
package io.thecodeforge.config;

import org.springframework.boot.autoconfigure.orm.jpa.HibernatePropertiesCustomizer;
import org.springframework.context.annotation.Configuration;
import java.util.Map;

/**
 * io.thecodeforge production-grade L2 Cache Configuration
 * Strategy: JSR-107 (JCache) with Ehcache 3 for distributed-ready caching.
 */
@Configuration
public class HibernateCacheConfig implements HibernatePropertiesCustomizer {
    
    @Override
    public void customize(Map<String, Object> hibernateProperties) {
        // 1. Explicitly enable the Second Level Cache
        hibernateProperties.put("hibernate.cache.use_second_level_cache", "true");
        
        // 2. Enable Query Cache (Caches results of JPQL/Criteria queries)
        hibernateProperties.put("hibernate.cache.use_query_cache", "true");
        
        // 3. Define the Region Factory using JCache (Standardized via JSR-107)
        hibernateProperties.put("hibernate.cache.region.factory_class", 
                  "org.hibernate.cache.jcache.internal.JCacheRegionFactory");
        
        // 4. Point to the specific Ehcache XML configuration
        hibernateProperties.put("hibernate.javax.cache.uri", "classpath:ehcache.xml");
        
        // 5. Missing identifiers strategy: generate exception if entity is not found in cache
        hibernateProperties.put("hibernate.cache.use_minimal_puts", "true");
    }
}
▶ Output
Hibernate L2 Cache initialized using JCacheRegionFactory with Ehcache 3 provider.
💡Key Insight:
L1 cache is always 'on' and cannot be disabled. It acts as a transactional buffer. L2 cache, however, requires an external provider like Ehcache, Hazelcast, or Infinispan to manage the data outside the Hibernate process.

Common Mistakes and How to Avoid Them

When learning Hibernate Caching, most developers fall into the trap of over-caching or failing to manage the cache lifecycle. A frequent 'gotcha' is performing bulk updates directly via SQL (Native or JPQL); this bypasses the Hibernate cache, leading to 'stale data' where the cache holds old values while the database has been updated.

At io.thecodeforge, we advocate for the 'Cache-Aside' or 'Read-Through' mindset. Another mistake is enabling L2 cache for highly volatile data. If an entity changes every second, the overhead of invalidating the L2 cache across a cluster (if using Hazelcast/Redis) will actually make your application slower than hitting the database directly.

io/thecodeforge/entities/Product.java · JAVA
1234567891011121314151617181920212223242526272829303132333435363738
package io.thecodeforge.entities;

import jakarta.persistence.*;
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
import java.math.BigDecimal;

/**
 * io.thecodeforge best practice: Entity-level caching configuration.
 */
@Entity
@Table(name = "forge_products")
@Cacheable
// NONSTRICT_READ_WRITE is ideal if your app tolerates occasional stale data
// and updates aren't happening concurrently in a heavy way.
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE, region = "product_cache")
public class Product {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @Column(nullable = false)
    private String sku;
    
    private String name;
    
    private BigDecimal price;

    // Standard Getters/Setters ignored for brevity
    
    /**
     * Production-grade Tip: Use versioning to help Hibernate manage 
     * optimistic locking and cache invalidation correctly.
     */
    @Version
    private Long version;
}
▶ Output
Entity 'Product' is mapped to 'product_cache' region with READ_WRITE concurrency.
⚠ Watch Out:
The most common mistake is ignoring the 'Concurrency Strategy'. Choosing 'READ_ONLY' for data that you eventually update will result in a runtime exception. Always align your strategy with your data's lifecycle.
FeatureFirst Level Cache (L1)Second Level Cache (L2)
ScopeSession Level (Private to one thread)SessionFactory Level (Shared across threads)
AvailabilityMandatory (Always on, cannot disable)Optional (Must be explicitly enabled)
LifecycleEnds with the Session/TransactionEnds with the SessionFactory/Application
PerformanceExtremely Fast (Local HashMap)Fast (Provider-dependent, often off-heap)
Stale Data RiskLow (Short-lived isolation)Higher (Requires robust eviction policies)
Data StorageStores entity objectsStores hydrated state (deconstructed data)

🎯 Key Takeaways

  • L1 cache is the Session-level buffer that ensures repeatable reads and identity preservation within a single transaction.
  • L2 cache is the cross-session store that drastically reduces database load for common entities across the entire application lifecycle.
  • The Query Cache does not store entities; it stores the IDs of entities matching your query. You must have Entity Caching enabled for Query Caching to be truly efficient.
  • Always use a mature Cache Provider (like Ehcache 3) to manage the memory footprint, eviction (LRU/LFU), and TTL of your L2 cache.
  • Be wary of stale data: ensure that any external database modifications are accounted for in your eviction strategy or via manual cache eviction.

⚠ Common Mistakes to Avoid

    Not clearing the Session in large batch jobs. Since L1 cache grows with every object loaded, processing 100k records in one transaction will cause an OutOfMemoryError. Use session.flush() and session.clear() every 50-100 records.

    00 records.

    Forgetting to enable Query Caching. Even if L2 is enabled for entities, Hibernate won't cache query results unless 'hibernate.cache.use_query_cache' is true AND you call .setCacheable(true) on the specific Query or Criteria object.

    ria object.

    Using L2 cache for tables frequently updated by external systems (e.g., legacy stored procedures). This leads to consistency issues because Hibernate's L2 cache isn't aware of out-of-band DB changes.

    DB changes.

    Choosing the wrong Concurrency Strategy. Using TRANSACTIONAL when your cache provider doesn't support JTA, or using READ_ONLY for mutable entities, will lead to deployment failures.

    t failures.

Interview Questions on This Topic

  • QExplain the 'N+1 Select Problem' and how the First Level Cache helps (or fails to help) in resolving it.
  • QWhat is the difference between session.evict(entity) and session.clear() in Hibernate? When would you use one over the other in a production environment?
  • QCompare the four CacheConcurrencyStrategy types: READ_ONLY, NONSTRICT_READ_WRITE, READ_WRITE, and TRANSACTIONAL. Which is best for a high-concurrency e-commerce site?
  • QHow does the L2 cache handle entity relationships (One-to-Many, Many-to-One)? Does caching a parent entity automatically cache its children?
  • QHow do you ensure the L2 cache is invalidated when a bulk update query is executed? Explain the use of the @Modifying and @Query annotation with clearAutomatically = true in Spring Data JPA.
🔥
Naren Founder & Author

Developer and founder of TheCodeForge. I built this site because I was tired of tutorials that explain what to type without explaining why it works. Every article here is written to make concepts actually click.

← PreviousHQL vs JPQL vs Native SQLNext →Hibernate N+1 Problem and How to Fix It
Forged with 🔥 at TheCodeForge.io — Where Developers Are Forged