为啥这两个 NHibernate 查询会产生不同的结果?

Posted

技术标签:

【中文标题】为啥这两个 NHibernate 查询会产生不同的结果?【英文标题】:Why do these two NHibernate queries produce different results?为什么这两个 NHibernate 查询会产生不同的结果? 【发布时间】:2011-06-01 18:16:04 【问题描述】:

这是一个 M:N 关系,集合在 NHibernate 中被映射为一个集合。

我们之前使用的条件查询“有效”,但它没有正确填充技能集合,因为只有第一个/寻找的技能被删除,即使员工有多种技能。

我将其更改为 LINQ 查询,它解决了问题,正确获取了该员工的所有技能。

Dim sId = 1 ' Just to have one for example
Dim lstEmployees = Session.CreateCriteria(Of Employee)() _
                    .CreateAlias("Skills", "s", NHibernate.SqlCommand.JoinType.LeftOuterJoin) _
                    .Add(Expression.Or(Expression.Eq("PrimarySkillId", sId),
                                       Expression.Eq("s.Id", sId))) _
                    .SetResultTransformer(New DistinctRootEntityResultTransformer()) _
                    .List(Of Employee)()

' Returns correct employees but only their first skill in the Skills collection, even if they have more than one

Dim lstEmployees = (From e In Session.Query(Of Employee)()
                  Where e.PrimarySkillId =sId OrElse e.Skills.Any(Function(s) s.Id = sId)
                  Select e Distinct).Fetch(Function(e) e.Skills).ToList()

' Returns correct employees and their Skills collection contains all of their skills

有人了解这两个看似等效的查询有什么不同吗?

【问题讨论】:

你对比过两者生成的SQL查询吗?这应该会给你一个关于问题所在的提示。 是的,但我不确定为什么它在代码中看起来与我相同时会有所不同。第二个查询使用了一个EXISTS句子查询,第一个根本没有这样做。 【参考方案1】:

首先,第一个查询在检索所有行后进行了不同的分离,而第二个查询实际上执行了select distinct ...。可能发生的情况是,它只用检索到的一项技能来为 Employee 模型补充水分

要使第一个查询实际执行select distinct ...,您需要使用投影。

类似

Session.CreateCriteria(Of Employee)() _
       .CreateAlias("Skills", "s", NHibernate.SqlCommand.JoinType.LeftOuterJoin)
       .Add(Expression.Or(Expression.Eq("PrimarySkillId", sId),
                          Expression.Eq("s.Id", sId))) _
       .SetProjection(Projections.Distinct("Id")) _
       .SetFetchMode("s", FetchMode.Eager)

可能会起作用。或者,您可以尝试使用 DistinctRootEntityResultTransformer 设置获取模式

Session.CreateCriteria(Of Employee)() _
       .CreateAlias("Skills", "s", NHibernate.SqlCommand.JoinType.LeftOuterJoin)
       .Add(Expression.Or(Expression.Eq("PrimarySkillId", sId),
                          Expression.Eq("s.Id", sId))) _
       .SetResultTransformer(Transformers.DistinctRootEntityResultTransformer) _
       .SetFetchMode("s", FetchMode.Eager)

【讨论】:

我看看这是否有效;我曾尝试过SetFetchMode,但我在CreateAlias 之前做过,我做过SetFetchMode("Skills", FetchMode.Eager)。它似乎不起作用,所以也许它最后出现或使用别名“s”很重要? 谢谢 Vadim,我两种方法都试过了,都不管用。对于第一个,它似乎只选择了 Id 属性,不能将其转换为员工列表。对于第二个,它仍然是具有相同结果的相同查询。我想知道,根据 LINQ,它是一个 EXISTS 子查询。也许一个独立的标准会起作用?不过,我不知道该怎么做。 @subkamran,这将是我的下一个建议,但这些有点复杂。 LINQ 工作正常,我对此表示满意,但 FetchMany 在 NH3.1 和 VB.NET 中已损坏。我想获取一些孙子记录,但在他们修复它之前我不能,所以这就是为什么我有兴趣让它在 Criteria 中工作。

以上是关于为啥这两个 NHibernate 查询会产生不同的结果?的主要内容,如果未能解决你的问题,请参考以下文章

当 CacheMode=Ignore 时,为啥 NHibernate.ISession.CreateQuery 会返回与 CreateSQLQuery 不同的东西?

Google Location API vs. Maps:为啥相同的查询会产生不同的结果?

为啥 NHibernate Linq 会重复结果?

为啥 Spark 以不同的方式解释这两个查询?

为啥 Snowflake 中这两个相似的查询具有非常不同的性能?

NHibernate QueryOver 与 Fetch 产生多个 sql 查询和数据库命中