Debugging Hibernate Second-Level Cache Misses with Ehcache
Your Hibernate second-level cache is enabled but database hits persist. Learn how entity regions, concurrency strategies, and query caching interact, with a concrete Ehcache example and verification steps.
21 May 2026, 06:22 UTC

The Problem: Cache Enabled, Database Still Hit
You added hibernate.cache.use_second_level_cache=true, annotated an entity with @Cacheable, and restarted the application. Yet the logs still show select ... from currency where code=? for the same identifiers on every request. The second-level cache is not a switch that caches everything automatically; it stores data in named regions, and an entity only enters a region if its mapping and access pattern allow it.
How Entity Regions Work
The second-level cache sits between the session and the database. When Hibernate loads an entity by ID, it first checks the entity's cache region. A region is keyed by entity name and identifier. If the entry is missing—a cache miss—Hibernate loads the row, puts it in the region, and returns it. On the next request for the same ID, the region returns the cached instance without a SQL query.
This behavior depends on the concurrency strategy. READ_ONLY is for immutable data and throws if the application attempts an update. READ_WRITE uses locks to keep the cache consistent after writes, which adds overhead. NONSTRICT_READ_WRITE does not guarantee that concurrent readers see the same version, but it avoids some locking cost.
Worked Example: Caching a Reference Entity
Suppose you have a Currency table that changes rarely. This is a good candidate for READ_ONLY. The example below assumes the legacy Ehcache region factory; adjust the factory class and XML schema if you use Hibernate 6.x with JCache.
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-ehcache</artifactId>
<version>{hibernate.version}</version>
</dependency>
ehcache.xml:
<ehcache>
<cache name="com.example.Currency"
maxElementsInMemory="1000"
timeToLiveSeconds="3600"
eternal="false"/>
</ehcache>
Entity:
@Entity
@Cacheable
@Cache(usage = CacheConcurrencyStrategy.READ_ONLY)
public class Currency {
@Id
private String code;
private String name;
// getters and setters
}
Hibernate properties:
hibernate.cache.use_second_level_cache=true
hibernate.cache.region.factory_class=org.hibernate.cache.ehcache.EhcacheRegionFactory
hibernate.generate_statistics=true
Run the application and load Currency by code twice. The first load hits the database; the second should hit the cache. Check the statistics bean or logs for second-level cache hits increasing while second-level cache misses stays flat.
Query Cache Is a Separate Region
Entity caching does not cache query results. If you run select c from Currency c repeatedly, Hibernate still executes the SQL unless you enable the query cache and call query.setCacheable(true). Query results are stored in a query-result region and are invalidated when any entity in the result set changes, which makes them expensive for frequently updated tables.
Trade-Offs and Limitations
READ_ONLYfails at runtime if the application updates the entity. Use it only for truly immutable reference data.READ_WRITEintroduces lock management overhead and can deadlock in clustered configurations without distributed locks.- Heap usage grows with the number of cached entities. Configure eviction in
ehcache.xmland monitor heap pressure. - Composite keys and natural IDs require explicit region naming and, for natural IDs,
@NaturalIdCache.
How to Verify the Result
- Enable
hibernate.generate_statistics=true. - Access the same entity ID twice in the same transactional boundary or across sessions.
- Inspect
org.hibernate.stat.internal.ConcurrentStatisticsImplor log output for second-level cache hit and miss counts. - If misses keep rising, confirm the entity is annotated, the region name in
ehcache.xmlmatches the entity class name, and the concurrency strategy permits the operation.
Start with one read-heavy entity, measure the hit ratio, and expand only after the cache shows a sustained miss-to-hit improvement.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.