NHibernate ICriteria查询是缓存还是放入身份映射?

时间:2011-01-28 15:04:46

标签: nhibernate caching icriteria identity-map

使用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:我添加了一些关于正在执行的最后一段查询的额外解释。

6 个答案:

答案 0 :(得分:2)

获取/加载使用第一级缓存,这就是为什么你没有看到第二个调用db的原因。查询不使用第一级缓存。但是,您可以设置查询以使用第二级缓存。详情请见here

更新可能发生的是查询正在进行2阶段加载。所以它获取结果集,但也检查第一级缓存以查看是否存在任何实体。如果是,则返回缓存对象。请参阅NHibernate.Loader.Loader.GetRow方法。 这是相关的一行:

//If the object is already loaded, return the loaded one
obj = session.GetEntityUsingInterceptor(key);

答案 1 :(得分:2)

嗯,我已经学到了很多关于NHibernate的知识,我现在可以自己回答这个问题: ICriteria查询返回NHibernate提取的对象列表。 NHibernate在与第一级缓存中的对象逐个匹配之前不知道返回哪些对象。如果该项目已在第一级缓存映射中,则从数据库中读取的项目将被丢弃。如果它不在身份映射中,则将该项放入第一级缓存中。

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

答案 2 :(得分:1)

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

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

修改:可以找到更多信息here

答案 3 :(得分:0)

我不确定为什么会运行第二个查询,但NHibernate的预期行为是,如果您通过同一会话中的ID请求相同的对象,则会获得第一级查询。

答案 4 :(得分:0)

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

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

编辑:

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

答案 5 :(得分:0)

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

此外,根据文档:

,您只能将查询缓存用于二级缓存
  

因此,查询缓存应始终与二级缓存一起使用。

来自here