NHibernate:在主细节中解决N + 1,其中细节具有多对一的关系?

时间:2012-04-27 08:18:51

标签: nhibernate query-optimization lazy-loading eager-loading

我有一个订单,其中包含OrderLines的集合。每个OrderLine都与产品有多对一的关系。对于所有订单,我有一个搜索窗口 - 默认情况下 - 显示网格中的所有订单。使用同一个窗口我可以过滤包含给定产品的所有订单。我们的客户每天生成约700个订单,订单平均约为35行。

在搜索窗口中,我只显示存储在Order对象本身中的信息。但是,如果我过滤特定的产品,它会将我的延迟加载关系初始化为OrderLines的集合以及我的OrderLine中的多对一产品。逐行查询每个订单行,对于每一行,都会对产品表进行单独查询。

所以平均而言,每个订单可以获得35个查询来初始化所有OrderLines,再加上35个查询来获取与该订单相关联的产品。

一次加载所有订单行及其相关产品的最佳方式是什么(如果不是每个订单都可以订购所有订单行)?我知道我可以将我的多对一映射到订单行中的产品作为fetch ="加入",但这仍然会留下35个单一查询来获取订单行+产品。

仅供参考:目前我把所有东西都加载了。

有什么建议吗?我可以在这里使用Future吗?

问候,特德

3 个答案:

答案 0 :(得分:3)

您的batch-size设置为什么?

按原样保留延迟加载,并在两个集合映射中将其设置为35,这应该将1 + 35 + 35个查询减少到1 + 1 + 1。

<bag ... batch-size='35' ..> 

同样如Stefan所指出,你也需要在课堂上设置batch-size='35'

答案 1 :(得分:0)

在这种情况下,我不会检索订单实体本身。 我会创建一个'View'类,通过使用NHibernate Projects,我将返回那些仅包含您感兴趣的信息的'View'类,并将其放在概述表单或页面中。 搜索时,您可以执行一个新查询(只有一个),它会检索您感兴趣的“OrderView”实例。

当您想要编辑订单或查看其详细信息时,我会加载该实体。

答案 2 :(得分:0)

您是否在已加载实体的内存中执行过滤操作?通常,您只想对包含相关产品的订单行的订单执行查询。那么你只需要根据查询结果填充你的窗口。没有必要遍历所有订单和所有订单行来填充仅包含一小部分数据的窗口。

在数据库服务器上进行过滤通常比从服务器获取所有数据并在C#中进行过滤要快得多。

我的描述并不完全清楚:鉴于您已经过滤了要显示的订单,您是否需要访问订单行和产品?
- 如果没有,你可以采用上述解决方案 - 如果是,您可以对已过滤的订单查询执行联接提取,以直接将订单行与订单一起加载。加入提取不仅可以用于&lt;多对一&gt;但也适用于&lt;一对多&gt;所以你可以一起加载一些根实体和他们的孩子。如果根实体具有许多,多列和多个子节点,则存在一些缺点,因为查询冗余地为每个子节点检索根实体的数据。然而,在许多情况下(取决于整个系统配置),连接获取的好处大大超过了成本。

在您查询数据并且事先不清楚以下代码实际需要哪些数据的情况下,使用批量选择(使用&lt; batch-size&gt;)可以帮助减少数据库往返次数。