如何使NHibernate的缓存中提取子集合?缓存、NHibernate

2023-09-02 23:44:05 作者:盯着贞子唱咆哮

我有一个非常简单的标准查询提取子集合,像这样:

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>();

使用NH教授,我已经验证这使得只有一个往返到数据库(如预期)与冷缓存;然而,在连续执行,它仅检索订单从缓存中,然后点击使用SELECT(N + 1)数据库在图中每一个孩子的实体,如

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.

我已经使用SysCache,SysCache2尝试这样做,甚至哈希表缓存提供商和我似乎总是得到同样的行为(NH版本3.2.0)。

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:

NH-195:儿童藏品没有被存储在二级缓存 Syscache2第2级缓存:儿童科尔。对象重新查询 SysCache和SysCache2之间古怪的差异 NHibernate - 谨防inadvisably应用缓存策略(Ayende - 当然他只困扰谈不上什么的没有的做,而不是如何解决它...) NH-195: Child collections are not being stored in the second level cache Syscache2 2nd level cache: Child coll. objects requeried Weird differences between SysCache and SysCache2 NHibernate – Beware of inadvisably applied caching strategies (Ayende - of course he only bothers to mention what not to do, not how to fix it...)

不过,这一切似乎都被固定在很久以前,我也得到了同样的不良行为,无论我使用的人员。

However, these all seem to have been fixed a long time ago, and I get the same bad behaviour regardless of which provider I use.

我通过SysCache和SysCache2 而且似乎没有任何东西我失踪。我试图加入 cacheRegion 行到的Web.config 文件的查询中涉及的所有表,但它不'T改变任何东西(和AFAIK这些元素只是为了的无效的缓存,所以他们不应该反正无所谓)。

I've read through the NHForge documentation on SysCache and SysCache2 and there doesn't seem to be anything I'm missing. I've tried adding cacheRegion lines to the Web.config file for all tables involved in the query, but it doesn't change anything (and AFAIK those elements are just to invalidate the cache, so they shouldn't matter anyway).

通过所有这些,所有的似乎是固定/解决超历史的问题,我想这没可能的还是的是NHibernate的一个bug,它必须是东西,我做错误。不过什么?

With all of these super-old issues that all seem to be fixed/resolved, I figure this can't possibly still be a bug in NHibernate, it must be something that I'm doing wrong. But what?

有没有我需要的时候取相结合NHibernate的说明与二级缓存做一些特别的东西?我缺少的是在这里吗?

Is there something special I need to do when combining fetch instructions in NHibernate with the second-level cache? What am I missing here?

推荐答案

我还是设法想出解决办法,这样其他人终于可以得到一个明确的答案:

I did manage to figure this out, so other folks can finally get a straight answer:

要概括起来讲,我一直困惑于在二级缓存和查询缓存之间的差别了一会儿;贾森的回答是技术上是正确的,但不知何故没有点击我。这是我会怎样解释:

To sum it up, I've been confused for a while on the difference between the second-level cache and the query cache; Jason's answer is technically correct but it somehow didn't click for me. Here is how I would explain it:

查询缓存保持跟踪的哪些实体的是通过查询发射。它的没有的缓存整个结果集。这是做的相当于 Session.Load 在延迟加载实体;它知道/预期存在,但不跟踪的任意的有关它的其他信息,除非特别要求,此时它会实际加载的真正的实体。

The query cache keeps track of which entities are emitted by a query. It does not cache the entire result set. It's the equivalent of doing a Session.Load on a lazy-loaded entity; it knows/expects that one exists but doesn't track any other information about it unless specifically asked, at which point it will actually load the real entity.

二级缓存跟踪实际的数据的每个实体。当NHibernate的需要加载的任何实体,其ID(凭借 Session.Load Session.Get ,懒惰-loaded关系,或在上面的例子中,一个实体的参考这是一个高速缓存查询的一部分),它看起来在二级缓存第一。

The second-level cache tracks the actual data for each entity. When NHibernate needs to load any entity by its ID (by virtue of a Session.Load, Session.Get, lazy-loaded relationship, or, in the case above, an entity "reference" that's part of a cached query), it will look in the second-level cache first.

当然,这是非常合情合理的后见之明,但是当你听到的术语查询缓存和二级缓存被用于几乎互换在很多地方它不是那么明显了。

Of course this makes perfect sense in hindsight, but it's not so obvious when you hear the terms "query cache" and "second-level cache" being used almost interchangeably in so many places.

基本上有两个组,每组两个设置,你需要为了看到查询缓存预期的结果进行配置:

Essentially there are two sets of two settings each that you need to configure in order to see the expected results with query caching:

在XML配置,这意味着将以下两行:

In XML configuration, this means adding the following two lines:

<property name="cache.use_second_level_cache">true</property>
<property name="cache.use_query_cache" >true</property>

在功能NHibernate,它是这样的:

In Fluent NHibernate, it's this:

.Cache(c => c
    .UseQueryCache()
    .UseSecondLevelCache()
    .ProviderClass<SysCacheProvider>())

请注意, UseSecondLevelCache 以上,因为(在此张贴的时间)是永远上的