我知道在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选择(这看起来很常见)?
答案 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的内部工作有一点了解,当你编写新的查询或标准时,它很容易被忽略。更通用的解决方案更可取。