I've got a fairly simple criteria query that fetches child collections, like so:
var order = Session.CreateCriteria<Order>()
.Add(Restrictions.Eq("Id", id))
.SetFetchMode("Customer", FetchMode.Eager)
.SetFetchMode("Products", FetchMode.Eager)
.SetFetchMode("Products.Category", FetchMode.Eager)
.SetCacheable(true)
.UniqueResult<Order>();
Using NH Prof, I've verified that this makes just one round trip to the database (as expected) with a cold cache; however, on successive executions, it retrieves only the Order from the cache and then hits the database with a SELECT(N+1) for every child entity in the graph, as in:
Cached query: SELECT ... FROM Order this_ left outer join Customer customer2 [...]
SELECT ... FROM Customer WHERE Id = 123;
SELECT ... FROM Products WHERE Id = 500;
SELECT ... FROM Products WHERE Id = 501;
...
SELECT ... FROM Categories WHERE Id = 3;
And so on and so forth. Clearly it's not caching the whole query or graph, only the root entity. The first "cached query" line actually has all of the join conditions that it's supposed to - it's definitely caching the query itself correctly, just not the entities, apparently.
I've tried this using the SysCache, SysCache2, and even HashTable cache providers and I always seem to get this same behaviour (NH version 3.2.0).
Googling turned up a number of ancient issues, such as: