Hibernate provides two levels of caching to improve performance by reducing database queries:
| Cache Level | Scope | Default | Description |
|---|---|---|---|
| First-Level Cache (L1) | Session | Always enabled | Per-session cache. Entities loaded in a session are cached for the session's lifetime. |
| Second-Level Cache (L2) | SessionFactory | Disabled | Shared across sessions. Requires a cache provider (EHCache, Infinispan, etc.). |
| Query Cache | SessionFactory | Disabled | Caches query results. Works with L2 cache. |
Session session = sessionFactory.openSession();
// First get: hits the database
User user1 = session.get(User.class, 1L);
System.out.println("First get: " + user1.getUsername()); // SQL executed
// Second get: served from L1 cache (same session)
User user2 = session.get(User.class, 1L);
System.out.println("Second get: " + user2.getUsername()); // NO SQL - from cache
// Same object reference
System.out.println(user1 == user2); // true
// Clear L1 cache
session.evict(user1); // Remove specific entity from cache
// session.clear(); // Clear entire L1 cache
// After evict: hits database again
User user3 = session.get(User.class, 1L);
System.out.println("After evict: " + user3.getUsername()); // SQL executed again
session.close();
// L1 cache is destroyed when session closes
<!-- Enable L2 cache in hibernate.cfg.xml -->
<property name="hibernate.cache.use_second_level_cache">true</property>
<property name="hibernate.cache.use_query_cache">true</property>
<property name="hibernate.cache.region.factory_class">
org.hibernate.cache.jcache.JCacheRegionFactory
</property>
<!-- pom.xml dependency -->
<!--
<dependency>
<groupId>org.hibernate.orm</groupId>
<artifactId>hibernate-jcache</artifactId>
<version>6.4.0.Final</version>
</dependency>
<dependency>
<groupId>org.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>3.10.8</version>
</dependency>
-->
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
// Cache strategies:
// READ_ONLY: For data that never changes (best performance)
// READ_WRITE: For data that changes occasionally (uses soft locks)
// NONSTRICT_READ_WRITE: No strict locking (slight risk of stale data)
// TRANSACTIONAL: Full transactional cache (JTA required)
@Entity
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class Product {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private double price;
}
// Cache a collection
@Entity
public class User {
@OneToMany(mappedBy = "user")
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
private List<Order> orders;
}
// Using query cache
Session session = sessionFactory.openSession();
List<Product> products = session.createQuery("FROM Product", Product.class)
.setCacheable(true) // Enable query cache
.setCacheRegion("productCache") // Optional: named cache region
.list();
session.close();
Hibernate caching changes when a read reaches the database. First-level state belongs to one Session, while second-level regions may be shared across Sessions and requests.
A hit is valuable only when reused data is still valid. Define region scope, concurrency strategy, eviction, and invalidation before enabling query caching or caching frequently updated entities.
The query may not be cacheable, the entity region may have expired, the data may have been invalidated, or the lookup may require association rows that are not cached. The first-level cache is Session-scoped; the second-level cache stores eligible entity state across Sessions.
Avoid aggressively caching highly volatile rows, security-sensitive decisions, inventory values, or data updated outside Hibernate unless invalidation is reliable. Caching works best for frequently read, relatively stable reference data.
Bulk HQL operates directly in the database and bypasses normal per-entity dirty checking. Managed objects already loaded in the Session can still contain old values, and caches may need invalidation. Clear or refresh the persistence context after bulk operations and understand the cache provider’s invalidation behavior before mixing bulk updates with cached entities.
Explore 500+ free tutorials across 20+ languages and frameworks.