4

使用 NHibernate,我通常使用 Get() 或 Load() 方法查询单个记录(取决于我是否需要代理):

SomeEntity obj = session.Get<SomeEntity>(new PrimaryKeyId(1));

现在,如果我执行此语句两次,如下例所示,我只看到在我的单元测试中执行了一个查询:

SomeEntity obj1 = session.Get<SomeEntity>(new PrimaryKeyId(1));
SomeEntity obj2 = session.Get<SomeEntity>(new PrimaryKeyId(1));

到目前为止,一切都很好。但是当使用 ICriteria 查询获取相同的对象时,我注意到一些奇怪的行为。在下面查看我的代码:我得到了第一个对象实例。然后我将一个属性的值更改为 10(数据库中的值为 8),获取另一个实例,最后检查第二个对象实例的值。

//get the first object instance.
SomeEntity obj1 = session.CreateCriteria(typeof(SomeEntity))
                         .Add(Restrictions.Eq("Id", new PrimaryKeyId(1)))
                         .UniqueResult<SomeEntity>();

//the value in the database and the property is 8 at this point. Let's set it to 10.
obj1.SomeValue = 10;

//get the second object instance.
SomeEntity obj2 = session.CreateCriteria(typeof(SomeEntity))
                         .Add(Restrictions.Eq("Id", new PrimaryKeyId(1)))
                         .UniqueResult<SomeEntity>();

//check if the values match.
Assert.AreEqual(8, obj2.SomeValue);

现在,由于某种原因,断言失败,因为值是 obj2 的 10,即使我使用新查询请求对象。有趣的是,根据我的单元测试输出窗口执行了 2 个完全相同的选择查询。我的问题:如果从一级缓存中获取第二个对象,为什么会执行 2 个查询?

我错过了什么还是这是一个错误?

问候,特德

编辑 #1:使用 NHibernate v2.1.2GA 编辑 #2:我在最后一段中添加了一些关于正在执行的 2 个查询的额外解释。

4

6 回答 6

3

好吧,在了解了有关 NHibernate 的更多信息后,我现在可以自己回答这个问题:ICriteria 查询返回 NHibernate 获取的对象列表。NHibernate 不知道返回了哪些对象,直到它们与一级缓存中的对象一一匹配。如果该项目已经在第一级缓存映射中,则从数据库中读取的项目将被丢弃。如果它不在身份映射中,则将该项目放入一级缓存。

另一个“啊哈!” 时刻:假设您第一次运行查询,而数据库中有 5 行,所有行都被提取并放入一级缓存。现在随着时间的推移,又有 5 条记录被添加到表中,然后您重新运行查询。现在所有 10 条记录都已获取,但 NHibernate 看到其中 5 条已经在缓存中,并且只会添加后面的 5 条记录。所以基本上你什么都没有获取 5 条记录(只是为了将标识符与身份映射中的对象标识符匹配)。

于 2011-08-12T14:52:31.933 回答
2

获取/加载使用第一级缓存,这就是为什么你看不到第二个调用数据库的原因。查询不使用一级缓存。但是,您可以设置查询以使用二级缓存。在此处查看详细信息

UPDATE可能发生的是查询正在进行两阶段加载。所以它正在获取结果集,但也检查一级缓存以查看那里是否存在任何实体。如果他们这样做,那么它会返回缓存的对象。见NHibernate.Loader.Loader.GetRow方法。这是相关的行:

//If the object is already loaded, return the loaded one
obj = session.GetEntityUsingInterceptor(key);
于 2011-01-28T15:55:39.350 回答
1

AFAIK,只有“ Get”(也许Load)使用第一级缓存。

除非启用了二级缓存,否则使用 Criteria API 始终会导致查询命中数据库。

编辑:更多信息可以在这里找到

于 2011-01-28T15:17:43.890 回答
0

据我了解,使用 Criteria 时,您基本上是在对 NHibernate 说:“我想根据表达式过滤行”。当这样看时,NHibernate 无法知道查询是否总是从数据库返回相同的过滤行,因此它必须再次查询。

此外,根据文档,您只能将查询缓存与二级缓存一起使用:

所以查询缓存应该始终与二级缓存结合使用。

这里

于 2011-01-28T16:14:13.047 回答
0

我不确定为什么要运行第二个查询,但 NHibernate 的预期行为是,如果您从同一会话中按 ID 请求同一对象,您将获得第一级缓存。

于 2011-01-28T15:08:42.147 回答
0

NHibernate 可能会在第一个和第二个查询之间发布更新,以保护您免受并发问题的影响。正如 Frederik 指出的那样,您应该始终使用Get它的键来检索对象。

我很好奇,PrimaryKeyId包装器添加了什么?

编辑:

但是它正在工作(我的钱仍然在选择之前进行更新),这种行为是设计使然。如果要丢弃内存中的对象并从会话中加载它的新实例,则首先从会话中加载Evict原始对象。还有一种Refresh方法你可以试试。

于 2011-01-28T15:31:25.530 回答