Hibernate Caching — L1, L2, and Query Cache
Fast recall
Quick revision
Core answer
EntityManager/transaction, and lookups are by (type, id). L2 is SessionFactory-scoped, shared across sessions, opt-in, and requires a provider (Ehcache, Caffeine, Hazelcast, Infinispan). The query cache stores the ID list for a JPQL/criteria query and only pays off when L2 is enabled. The lookup chain is L1 -> L2 -> database, with the query cache adding query-cache -> L2 -> database. Misunderstanding scope, invalidation, or bulk-DML interaction is the most common source of stale-data bugs in Hibernate apps.Awareness of cache scoping, eviction semantics, and the risk of serving stale data in concurrent and multi-instance environments
Assuming the second-level cache is enabled by default, that it auto-syncs with the database, or that bulk JPQL updates evict it
Discuss cache region tuning, concurrency strategies (READ_ONLY vs READ_WRITE), distributed providers, and when a service-layer Redis cache beats Hibernate L2
Interview-ready guide
Interview answer
Hibernate has three cache layers, each with a different scope and lifecycle.
L1 — first level, per session:
- Map of
(type, id) -> entityinside oneEntityManager - Always on, cleared at transaction end
- Repeated
findByIdinside one transaction fires one SQL
L2 — second level, shared:
- Configured per entity with
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE) - Needs a provider — Ehcache, Caffeine, Hazelcast, Infinispan
- Survives transactions; any write through the EM invalidates the entry
- Disabled by default; bulk DML and external writers bypass it
Query cache — ID list cache:
- Stores result IDs keyed by query text + parameters
- Aggressive invalidation: any write to the entity type evicts every cached query
- Only useful with L2 enabled
When L2 helps versus hurts:
- Helps: reference data, single instance or distributed provider
- Hurts: frequently written entities, multi-instance without invalidation, external writers
Practical alternative:
- A Redis cache at the
@Cacheableservice layer is often simpler — explicit keys, easy invalidation, observable, decoupled from Hibernate
Hibernate Caching — L1, L2, and Query Cache
Direct Answer
Hibernate has three cache layers. L1 is the persistence context — always on, scoped to a single EntityManager/transaction, and lookups are by (type, id). L2 is SessionFactory-scoped, shared across sessions, opt-in, and requires a provider (Ehcache, Caffeine, Hazelcast, Infinispan). The query cache stores the ID list for a JPQL/criteria query and only pays off when L2 is enabled. The lookup chain is L1 -> L2 -> database, with the query cache adding query-cache -> L2 -> database. Misunderstanding scope, invalidation, or bulk-DML interaction is the most common source of stale-data bugs in Hibernate apps.
Code Example
Minimal working setup for Hibernate L2 with Caffeine and the query cache.
spring:
jpa:
properties:
hibernate:
cache:
use_second_level_cache: true
use_query_cache: true
region.factory_class: org.hibernate.cache.jcache.JCacheRegionFactory
javax.cache.provider: com.github.benmanes.caffeine.jcache.spi.CaffeineCachingProvider
generate_statistics: true
@Entity
@org.hibernate.annotations.Cache(
usage = CacheConcurrencyStrategy.READ_WRITE,
region = "products")
public class Product { /* ... */ }
public interface ProductRepo extends JpaRepository<Product, Long> {
@QueryHints(@QueryHint(name = HINT_CACHEABLE, value = "true"))
@Query("select p from Product p where p.category = :c")
List<Product> byCategory(@Param("c") String c);
}
@Service
public class PriceService {
@Autowired SessionFactory sf;
@Transactional
public int bumpPrices(double pct) {
int n = em.createQuery("update Product p set p.price = p.price * :f")
.setParameter("f", 1 + pct).executeUpdate();
sf.getCache().evict(Product.class);
return n;
}
}
Common Mistakes & Pitfalls
Assuming the second-level cache is enabled by default, that it auto-syncs with the database, or that bulk JPQL updates evict it
Check your understanding
Answer these without looking back at the article.
- Which CacheConcurrencyStrategy would you choose for a Product entity that is updated several times per day?
- How does a bulk JPQL UPDATE affect the second-level cache, and how do you handle it?
- What are the trade-offs between in-process caches like Caffeine and distributed caches like Hazelcast for L2?
- When would you prefer a service-layer Redis cache over Hibernate L2?
Related interview prep categories
2Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.