使用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:我添加了一些关于正在执行的最后一段查询的额外解释。
答案 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)
答案 3 :(得分:0)
我不确定为什么会运行第二个查询,但NHibernate的预期行为是,如果您通过同一会话中的ID请求相同的对象,则会获得第一级查询。
答案 4 :(得分:0)
NHibernate可能会在第一个和第二个查询之间发布更新,以保护您免受并发问题的影响。正如Frederik指出的那样,您应该始终使用Get
通过其密钥检索对象。
我很好奇,PrimaryKeyId
包装器的添加是什么?
编辑:
然而它正在工作(我的钱仍然在选择之前更新),这种行为是设计的。如果要丢弃内存中对象并从会话中加载新实例,则首先Evict
会话中的原始对象。您还可以尝试使用Refresh
方法。
答案 5 :(得分:0)
根据我的理解,当使用Criteria时,你基本上是对NHibernate说:“我想根据表达式过滤行”。 当看到这种方式时,NHibernate无法知道查询是否总是从数据库返回相同的已过滤行,因此它必须再次查询。
此外,根据文档:
,您只能将查询缓存用于二级缓存因此,查询缓存应始终与二级缓存一起使用。
来自here