【问题标题】:Avoiding N+1 selects using business rules in NHibernate在 NHibernate 中使用业务规则避免 N+1 选择
【发布时间】:2009-06-16 11:17:37
【问题描述】:

我知道在 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 必须以这种方式工作以确保正确的结果似乎是合理的。)

但是,根据我的业务规则(或您要如何称呼它们),一个商品不能属于来自多个客户的类别,所以实际上我知道第一个查询的结果实际上是完整的。

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

【问题讨论】:

    标签: nhibernate business-logic


    【解决方案1】:

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

    我的解决方案是将问题分为两个查询:首先获取属于该客户的商品的 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 的内部工作了解得太多,在编写新的查询或条件时很容易错过。更通用的解决方案会更好。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-05-19
      • 2015-11-28
      • 2011-07-12
      • 1970-01-01
      • 2023-03-02
      • 2011-08-03
      • 2011-12-07
      • 2016-05-16
      相关资源
      最近更新 更多