避免N + 1选择使用NHibernate中的业务规则

时间:2009-06-16 11:17:37

标签: nhibernate business-logic

我知道在Hibernate / NHibernate中避免N + 1选择问题的基本方法,但遇到了一个我找不到合适解决方案的问题的变种。

我映射了以下三个实体:Item,Category和Customer。项目与“类别”的多对多关联,“类别”与“客户”多对一关联。到目前为止,没有什么特别的。

我的应用程序中的标准查询是获取给定客户的所有项目。我使用以下标准执行此操作,尝试急切地获取项目的类别,以避免在检查项目的“类别”属性时选择N + 1:

ICriteria criteria = mySession.CreateCriteria(typeof(Item));
    .CreateCriteria("Categories", NHibernate.SqlCommand.JoinType.InnerJoin)
        .Add(Expression.Eq("Customer", c));
criteria.SetFetchMode("Categories", FetchMode.Eager);

return criteria.List();

然而,这不起作用,NHibernate仍会在稍后的每个项目中选择一个类别。

我相信NHibernate知道第一个查询的结果是在客户上过滤的,而查询返回的类别可能不完整,因此以后必须单独查询得到类别。 (这个假设是否正确?对我而言,NHibernate必须以这种方式工作才能确保正确的结果。)

但是,根据我的业务规则(或您想要称之为什么),项目不能属于多个客户的类别,因此实际上我知道第一个查询的结果实际上是完整的。< / p>

我的问题是:我能以任何方式告诉NHibernate这个业务规则吗?在这种情况下是否有另一种方法可以避免N + 1选择(这看起来很常见)?

1 个答案:

答案 0 :(得分:2)

将尝试回答我自己的问题,因为到目前为止我还没有得到任何答案。

我的解决方案是将问题分成两个查询:首先获取属于相关客户的项目的ID:

IQuery query = mySession.CreateQuery("select item.Id from Item as item "
    + "join item.Categories as category "
    + "join category.Customer customer "
    + "where customer.id=:id")
    .SetInt32("id", c.Id);
IList itemIds = query.List();

然后,我只需使用ID即可获取实际商品而不会对客户产生任何限制。这样NHibernate知道它可以从单个连接中获取所有类别,避免问题中提到的N + 1选择:

ICriteria criteria = mySession.CreateCriteria(typeof(MapItem))
    .SetFetchMode("Categories", FetchMode.Eager)
    .SetResultTransformer(new DistinctRootEntityResultTransformer())
    .Add(Expression.In("Id", itemIds));
IList items = criteria.List();

我无法想出任何可以将其简化为有效的单一查询的解决方案。此外,这种方法迫使程序员对NHibernate的内部工作有一点了解,当你编写新的查询或标准时,它很容易被忽略。更通用的解决方案更可取。