我们遇到的情况是,我们尝试使用QueryOver检索对象图的几个级别。因此,如果我们的顶级是ParentEntity,而我们的孩子是ChildEntitysA,ChildEntitysB和ChildEntitysC,那么我们可以使用以下代码来检索我们的图形。
var query = session.QueryOver<ParentEntity>(() => pAlias).Where(() => pAlias.Id == key).Future<ParentEntity>();
var queryA = session.QueryOver<ParentEntity>(() => pAlias).Left.JoinAlias(x => x.ChildEntitysA, () => caAlias).Where(() => pAlias.Id == key).Future<ParentEntity>();
var queryB = session.QueryOver<ParentEntity>(() => pAlias).Left.JoinAlias(x => x.ChildEntitysB, () => cbAlias).Where(() => pAlias.Id == key).Future<ParentEntity>();
var queryC = session.QueryOver<ParentEntity>(() => pAlias).Left.JoinAlias(x => x.ChildEntitysC, () => ccAlias).Where(() => pAlias.Id == key).Future<ParentEntity>();
return query.ToList();
这应该在一次调用数据库时生成4个SQL语句,并带回我们想要的内容。但是,它生成的SQL确实有一些低效率,因为所有子查询都将在SELECT语句中包含父级的所有列以及子实体的列,类似于:
SELECT parent.col0, parent.col1, parent.col2, parent.col3, .... parent.col99
FROM parent WHERE .....
SELECT parent.col0, parent.col1, parent.col2, parent.col3, .... parent.col99, childA.col0, childA.col1, childA.col2 .... childA.col99
FROM parent LEFT OUTER JOIN childA ON ....
SELECT parent.col0, parent.col1, parent.col2, parent.col3, .... parent.col99, childB.col0, childB.col1, childB.col2 .... childB.col99
FROM parent LEFT OUTER JOIN childB ON ....
SELECT parent.col0, parent.col1, parent.col2, parent.col3, .... parent.col99, childC.col0, childC.col1, childC.col2 .... childC.col99
FROM parent LEFT OUTER JOIN childC ON ....
但为父实体生成的查询已经包含了所有必要的数据!如果我们返回父项的倍数并且每个子查询的数据量相应地增加(特别是因为所涉及的实体在基础列的数量方面非常大),这确实成为一个效率问题。
那么,有没有什么办法可以强制nHibernate不生成SQL来为查询的每个部分返回所有父实体的值,只是将它限制为仅在SQL中返回最小的键列? Queryover甚至是针对这种情况的最佳API吗?
答案 0 :(得分:1)
我建议的方法是在这些组合中使用NHibernate功能的全部功能:
lazy
加载和batch-size
设置所以,在第一种情况下,我们即将创建一个QueryOver
(可能包括一些子查询)
它必须包含投影,这是必不可少的。它可以包括(很多)Left,Inner join,可以深度过滤......然后我们将得到缩小的SELECT语句,只包含必需的字段。这导致一个 SQL查询,必须将其转换为一些(未映射) DTO对象
预测和 DTO 意味着,最后我们必须使用ResultTransformer
,它会将所有即将到来的选定字段转换为DTO对象属性。< / p>
在第二种情况下,我们试图从 lazy 行为和batch-size
映射中获利。它看起来像这样:
1)类/实体
<class name="ParentEntity" batch-size="25" lazy="true">
...
2)收集
<bag name="Children" lazy="true" batch-size="25"
...
详见文档:19.1.5. Using batch fetching
我们在这里获得的是,我们可以创建仅返回轻型对象(已映射的)的软查询。这是第一个选择。然后,所有属性(多对一,一对多)都是批量加载的 - 但只有在需要访问它们时才会加载。
这导致多于1个SELECT子句,但也没有像 1 + N 这样的SELECT子句的膨胀。如果NHibernate会发现,session
已经加载了一些必需的属性......它将不再激活SELECT。
总结:这两种方法都是如此。尝试玩arround以了解您从第一个或第二个获得更多的情况。但是,无论如何都应该使用lazy="true"
和batch-size="25"
的映射
有关批量大小的更多链接: