Hibernate Second Level Cache
Complete guide to Hibernate Second Level Cache with architecture, cache providers, Redis/Ehcache integration, cache strategies, examples, performance optimization, and interview questions.
Second Level Cache (L2 Cache) is a cache shared across multiple Hibernate Sessions.
Unlike First Level Cache:
First Level Cache
↓
Session Scoped
Second Level Cache:
Application Scoped
Meaning:
Session 1
Session 2
Session 3
Session N
↓
Shared Cache
Multiple sessions can reuse cached data without querying the database.
Why Do We Need Second Level Cache?
Consider this scenario:
Session 1
Employee employee =
entityManager.find(Employee.class, 1L);
SQL:
SELECT * FROM employees WHERE id = 1;
Session ends.
Later:
Session 2
Employee employee =
entityManager.find(Employee.class, 1L);
Again:
SELECT * FROM employees WHERE id = 1;
Problem:
First Level Cache
Dies with Session
Database keeps getting hit repeatedly.
Second Level Cache solves this.
Real World Example
Imagine Netflix.
Movie metadata:
Movie Name
Genre
Duration
Rating
Millions of users request it.
Without cache:
Every Request
↓
Database
Disaster.
Instead:
Database
↓
Redis / Cache
↓
Millions of Requests
Hibernate L2 Cache works similarly.
Cache Levels Overview
flowchart TD
A["Application"]
B["First Level Cache"]
C["Second Level Cache"]
D["Database"]
A --> B
B -->|Miss| C
C -->|Miss| D
D --> C
C --> B
First Level vs Second Level Cache
| Feature | First Level Cache | Second Level Cache |
|---|---|---|
| Scope | Session | Application |
| Shared | No | Yes |
| Default | Enabled | Disabled |
| Lifetime | Session | Application |
| Storage | JVM Memory | Cache Provider |
| Configuration | None | Required |
Architecture
flowchart LR
A["Session 1"]
B["Session 2"]
C["Session 3"]
D["Second Level Cache"]
E["Database"]
A --> D
B --> D
C --> D
D --> E
Cache Providers
Hibernate itself does not store cache.
It delegates to providers.
Popular options:
| Provider | Usage |
|---|---|
| Ehcache | Most Popular |
| Redis | Distributed Cache |
| Hazelcast | Cluster Cache |
| Infinispan | Enterprise |
| Caffeine | Lightweight |
How Second Level Cache Works
First Request
employeeRepository.findById(1L);
Flow:
First Level Cache → Miss
Second Level Cache → Miss
Database → Hit
Store in L2 Cache
Return Entity
Second Request
Different Session:
employeeRepository.findById(1L);
Flow:
First Level Cache → Miss
Second Level Cache → Hit
Return Entity
No SQL.
Detailed Flow
flowchart TD
A["Find Entity"]
B["Check First Level Cache"]
C["Check Second Level Cache"]
D["Database"]
E["Store In L2 Cache"]
F["Return Entity"]
A --> B
B -->|Miss| C
C -->|Hit| F
C -->|Miss| D
D --> E
E --> F
Sample Entity
@Entity
@Table(name = "employees")
@Cacheable
@org.hibernate.annotations.Cache(
usage = CacheConcurrencyStrategy.READ_WRITE
)
public class Employee {
@Id
private Long id;
private String name;
private Double salary;
}
Enable Second Level Cache
application.yml
spring:
jpa:
properties:
hibernate:
cache:
use_second_level_cache: true
use_query_cache: true
Maven Dependency
Ehcache
<dependency>
<groupId>org.ehcache</groupId>
<artifactId>ehcache</artifactId>
</dependency>
Example
First Request
@Transactional
public Employee getEmployee() {
return repository.findById(1L)
.orElseThrow();
}
Generated SQL:
SELECT *
FROM employees
WHERE id = 1;
Second Request
Another transaction:
@Transactional
public Employee getEmployeeAgain() {
return repository.findById(1L)
.orElseThrow();
}
SQL:
No SQL
Served from cache.
Visualization
Request 1
User
↓
Hibernate
↓
Database
↓
Cache
--------------------------------
Request 2
User
↓
Hibernate
↓
Cache
↓
Response
Cache Region
Each entity has a cache region.
Example:
@Entity
@Cacheable
@Cache(
usage = CacheConcurrencyStrategy.READ_WRITE,
region = "employee-region"
)
public class Employee {
}
Cache Region Diagram
Second Level Cache
+--------------------+
| employee-region |
+--------------------+
+--------------------+
| customer-region |
+--------------------+
+--------------------+
| order-region |
+--------------------+
Cache Concurrency Strategies
Very important interview topic.
READ_ONLY
Best performance.
Only for immutable data.
@Cache(
usage = CacheConcurrencyStrategy.READ_ONLY
)
Example:
Country
State
Currency
Language
Never changes.
READ_WRITE
Most commonly used.
Supports updates safely.
@Cache(
usage = CacheConcurrencyStrategy.READ_WRITE
)
Best for:
Employee
Customer
Product
NONSTRICT_READ_WRITE
Allows stale data briefly.
Faster than READ_WRITE.
@Cache(
usage =
CacheConcurrencyStrategy.NONSTRICT_READ_WRITE
)
Good for:
Mostly Read Data
TRANSACTIONAL
Used with JTA.
Rarely used.
@Cache(
usage =
CacheConcurrencyStrategy.TRANSACTIONAL
)
Strategy Comparison
| Strategy | Updates | Consistency | Performance |
|---|---|---|---|
| READ_ONLY | No | High | Fastest |
| READ_WRITE | Yes | High | Good |
| NONSTRICT_READ_WRITE | Yes | Medium | Better |
| TRANSACTIONAL | Yes | Highest | Slower |
What Happens During Update?
Suppose:
employee.setSalary(200000);
Hibernate updates:
UPDATE employees
SET salary = 200000
WHERE id = 1;
Then:
Invalidate Cache
or
Update Cache Entry
depending on strategy.
Update Flow
flowchart TD
A["Update Entity"]
B["Database Update"]
C["Cache Update"]
D["Future Reads"]
A --> B
B --> C
C --> D
Query Cache
Entity Cache:
findById()
results cached.
Query Cache:
findAll()
results cached.
Enable:
hibernate:
cache:
use_query_cache: true
Example
Query query =
entityManager.createQuery(
"select e from Employee e");
query.setHint(
"org.hibernate.cacheable",
true
);
Entity Cache vs Query Cache
| Feature | Entity Cache | Query Cache |
|---|---|---|
| Stores | Entities | Query Results |
| findById | Yes | No |
| JPQL | No | Yes |
| Native Query | No | Optional |
Redis Architecture
Enterprise systems often use Redis.
flowchart LR
A["Application 1"]
B["Application 2"]
C["Application 3"]
D["Redis"]
E["Database"]
A --> D
B --> D
C --> D
D --> E
Benefits:
Shared Across Servers
Distributed
High Performance
Performance Example
Without Cache:
1000 Requests
1000 Database Calls
With Cache:
1000 Requests
1 Database Call
999 Cache Hits
What Should Be Cached?
Good Candidates:
✅ Country
✅ State
✅ Currency
✅ Product Catalog
✅ Employee Master Data
✅ Configuration Tables
Avoid Caching:
❌ Frequently Updated Data
❌ Real-Time Trading Data
❌ Session Data
❌ Highly Volatile Data
Common Problems
Stale Data
Database changed externally.
Cache contains old value.
Solution:
Cache Eviction
TTL
Refresh Strategy
Cache Stampede
Thousands of requests.
Cache expired.
Everyone hits DB simultaneously.
Solution:
Redis
Locking
Warm-up Cache
Best Practices
✅ Cache read-heavy entities
✅ Use READ_ONLY whenever possible
✅ Use Redis for distributed environments
✅ Monitor cache hit ratio
✅ Define cache regions properly
Interview Questions
Q1. What is Second Level Cache?
Application-wide shared Hibernate cache.
Q2. Is it enabled by default?
No.
Must be configured.
Q3. Difference between First and Second Level Cache?
First Level Cache
Session Scoped
Second Level Cache
Application Scoped
Q4. Which cache is checked first?
1. First Level Cache
2. Second Level Cache
3. Database
Q5. What is CacheConcurrencyStrategy?
Defines consistency behavior.
Examples:
READ_ONLY
READ_WRITE
NONSTRICT_READ_WRITE
TRANSACTIONAL
Q6. Which strategy is most used?
READ_WRITE
Q7. Which strategy gives best performance?
READ_ONLY
Complete End-to-End Flow
flowchart TD
A["Application Request"]
B["First Level Cache"]
C["Second Level Cache"]
D["Database"]
E["Store In Cache"]
F["Return Entity"]
A --> B
B -->|Miss| C
C -->|Miss| D
D --> E
E --> F
C -->|Hit| F
Summary
Second Level Cache is Hibernate's shared application-level cache.
Workflow:
Request
↓
First Level Cache
↓
Second Level Cache
↓
Database
Key Benefits:
✅ Reduced Database Load
✅ Faster Response Time
✅ Improved Scalability
✅ Better Throughput
Remember:
First Level Cache
=
Session Scope
Second Level Cache
=
Application Scope
In production systems, Redis + Hibernate Second Level Cache is one of the most powerful techniques for reducing database traffic and improving application performance.