Leveraging NHibernate’s Second‑Level Cache: A Practical Guide
Discover how to enable NHibernate’s second‑level cache, configure Ehcache or Redis, and avoid common pitfalls. A step‑by‑step example shows entity and query caching, invalidation, and region tuning for read‑heavy applications.
01 Jul 2025, 19:23 UTC

The Problem & Takeaway
In read‑heavy applications, NHibernate’s default per‑session cache forces a round‑trip to the database for every entity load, even when the same data is requested across sessions. The second‑level cache solves this by storing entity, collection, and query results in a shared cache region that survives beyond a single session. Enabling it correctly can cut database load by 70‑90%, but misconfiguration can lead to stale data, contention, or wasted memory.
How NHibernate’s Second‑Level Cache Works
NHibernate divides caching into two layers:
- First‑level cache – per‑session identity map (always enabled).
- Second‑level cache – shared across sessions, backed by an external provider such as Ehcache, Redis, or Microsoft.Extensions.Caching.Memory.
When a session loads an entity, NHibernate checks the second‑level cache first. If a cached entry exists and is still valid, the entity is returned directly. If not, NHibernate queries the database and then stores the result in the cache for future use.
Minimal Configuration Example
Below is a minimal hibernate.cfg.xml that enables the second‑level cache and configures Ehcache as the provider.
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
<session-factory>
<property name="connection.driver_class">NHibernate.Driver.SqlClientDriver</property>
<property name="connection.connection_string">Data Source=.;Initial Catalog=DemoDb;Integrated Security=True</property>
<property name="dialect">NHibernate.Dialect.MsSql2008Dialect</property>
<property name="cache.use_second_level_cache">true</property>
<property name="cache.use_query_cache">true</property>
<property name="cache.provider_class">NHibernate.Caches.EhCache.EhCacheProvider</property>
<property name="cache.default_cache_concurrency_strategy">read-write</property>
</session-factory>
</hibernate-configuration>
Place an ehcache.xml in the application’s root folder to define region settings (size, eviction policy, etc.). For example:
<ehcache>
<cache name="default"
maxEntriesLocalHeap="10000"
eternal="false"
timeToIdleSeconds="300"
timeToLiveSeconds="600"
overflowToDisk="false"
diskPersistent="false"/>
</ehcache>
Enabling Caching on Entities and Queries
Entity caching is declared in the mapping file or via attributes. Using Fluent NHibernate:
public class CustomerMap : ClassMap<Customer>
{
public CustomerMap()
{
Id(x => x.Id);
Map(x => x.Name);
Cache.ReadWrite(); // Enable second‑level cache for this entity
}
}
With XML mapping:
<class name="Customer" table="Customers" cache="read-write">
<id name="Id" column="CustomerId" generator-class="native"/>
<property name="Name" column="Name"/>
</class>
For query caching, set the cacheable flag on the IQuery or ICriteria instance:
var query = session.QueryOver<Customer>()
.Where(c => c.Name == "Alice")
.Cacheable(); // Mark query for caching
var customers = query.List();
Remember, query caching only stores the result set; the underlying entities are still fetched from the second‑level cache if available.
Cache Regions and Customization
By default, NHibernate uses the fully qualified class name as the region key. You can override this to group related entities or to apply specific eviction policies per region:
<class name="Order" table="Orders" cache="read-write" cache-region="orders.region">…</class>
Define the region in ehcache.xml with tailored settings:
<cache name="orders.region"
maxEntriesLocalHeap="5000"
timeToLiveSeconds="1200"/>
Invalidation & Consistency
NHibernate automatically invalidates cached entries when an entity is updated, inserted, or deleted through a session. The provider must support the chosen concurrency strategy:
- Read‑write – Guarantees strong consistency but can cause lock contention in high‑write scenarios.
- Non‑strict read‑write – Offers better performance for write‑heavy workloads but may serve stale data for a short window.
- Read‑only – No updates allowed; best for immutable data.
If external processes modify the database directly, the cache may become stale. In such cases, use a provider that supports automatic eviction via messaging (e.g., Redis pub/sub) or manually evict affected regions with ISessionFactory.Cache.EvictEntityRegion.
Common Pitfalls & Limits
- Forgetting
cache.use_second_level_cache– The cache is disabled by default; enable it explicitly. - Mis‑configured provider – Missing serialization or cluster settings can cause cache loss on restart or across nodes.
- Using read‑write on write‑heavy workloads – Leads to lock contention; switch to non‑strict or read‑only if appropriate.
- Unmarked queries – Query caching requires
Cacheable(); otherwise, each execution hits the DB. - Stale data from external updates – Ensure external changes trigger cache eviction or use a provider with automatic invalidation.
- Memory over‑commitment – Large cache regions can exhaust heap; monitor and tune
maxEntriesLocalHeap.
Quick Checklist & Verification Steps
- Set
cache.use_second_level_cacheto true. - Configure a provider (Ehcache, Redis, etc.) and supply a valid config file.
- Mark entities and queries with caching attributes or XML.
- Run a test: perform a query, then repeat it in a new session; the second run should skip the DB (check logs).
- Update an entity via NHibernate and read it again in a fresh session; the new value should appear instantly.
- Inspect provider logs (Ehcache’s
ehcache.xmlor Redis logs) for cache hit/miss statistics. - Monitor memory usage and eviction events to ensure the cache stays within desired limits.
Summary
Enabling NHibernate’s second‑level cache can dramatically reduce database load for read‑heavy scenarios. The key is correct configuration: choose a provider that matches your scaling needs, declare cache usage on entities and queries, and understand the chosen concurrency strategy. By following the checklist above and monitoring cache metrics, you can harness the performance benefits while avoiding common pitfalls.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.