为什么NHibernate不急于获取我的数据

时间:2011-08-03 06:20:59

标签: nhibernate eager-loading

我正在使用Nhibernate作为我的ORM。

我有一个与ControlDetail有一对多关系的“Control”类(即一个控件有很多controlDetails)。

在控件xml配置中,它具有以下

<bag name="ControlDetails" lazy="true" access="property" order-by="SortOrder asc"  cascade="all-delete-orphan" 
  table="ControlDetail">
  <key column="ControlID"/>
  <one-to-many class="ControlDetail"/>
</bag>

这样我相信除非另有说明,否则会延迟加载控件的控件。

我正在运行NHProf来尝试解决我们遇到的一些性能问题,它已经在这些类中找到了一个选择N + 1问题。

我们正在使用存储库DA层,我试图看看我是否可以添加一种方法,在需要时急切地获取数据并提出这个。

public T GetById<T>(Int32 id, List<string> fetch) where T : BaseObject
{
    T retObj = null;
    ISession session = EnsureCurrentSession();
    {
        ICriteria criteria = session.CreateCriteria(typeof (T));
        criteria.SetCacheable(true);
        criteria.Add(Expression.Eq("Id", id));

        foreach(var toFetch in fetch)
        {
            criteria.SetFetchMode(toFetch, FetchMode.Eager);
        }

        retObj = criteria.List<T>().FirstOrDefault();
    }

    return retObj;
}

*注意:我不喜欢存储库的设置,但是在我进入项目之前就已经完成了,所以我们现在必须坚持使用这种模式。

我这样称呼这个方法

public Control GetByIDWithDetail(int controlID)
{
    return DataRepository.Instance.GetById<Control>(controlID, new List<string>() {"ControlDetail"});
}

当我调试GetByID方法并查看retObj时,我可以看到已经填充了ControlDetails列表(虽然奇怪的是我还注意到没有setfetchmode设置列表正在填充)

即使使用此修复程序,NHProf也会使用以下行标识选择N + 1问题

List<ControlDetail> details = control.ControlDetails.ToList();

我到底错过了什么,如何停止此N + 1但仍然遍历controlDetails列表

编辑:xml配置看起来像这样(略微编辑以缩小)

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="DomainObjects" assembly="DomainObjects">
    <class name="Control" lazy="false" table="Control" optimistic-lock="version" select-before-update="true"  >
        <id name="Id" type="int" column="ControlID" access="property">
            <generator class="native" />
        </id>
    <version name="Version" column="Version" />
        <property name="AdministrativeControl" column="AdministrativeControl" access="property" not-null="true" />
    <property name="Description" column="ControlDescription" access="property" />
    <property name="Title" column="Title" access="property" />
    <property name="CountOfChildControls" access="property" formula="(select count(*) from Control where Control.ParentControlID = ControlID)" />

    <bag name="ControlDetails" lazy="true" access="property" order-by="SortOrder asc"  cascade="all-delete-orphan"
      table="ControlDetail">
      <key column="ControlID" />
      <one-to-many class="ControlDetail"  />
    </bag>

  </class>
</hibernate-mapping>

这个     

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="DomainObjects" assembly="DomainObjects">
    <class name="ControlDetail" lazy="false" table="ControlDetail" select-before-update="true" optimistic-lock="version">
        <id name="Id" type="int" column="ControlDetailID" access="property">
            <generator class="native" />
        </id>
    <version name="Version" column="Version" />
    <property name="Description" column="Description" access="property" not-null="true" />
    <property name="Title" column="Title" access="property" />

    <many-to-one name="Control" lazy="false" class="Control" column="ControlID" access="property"/>
  </class>
</hibernate-mapping>

2 个答案:

答案 0 :(得分:5)

渴望获取和急切加载之间存在很大差异。提取意味着:进入相同的查询。它有几个副作用,可能会打破它。急切加载意味着迫使NH立即加载它,而不是等到第一次访问它。它仍然使用其他查询加载,这会导致N + 1问题。

NH可能不会急切地获取因为该属性被称为ControlDetails,但您传递ControlDetail作为参数。

另一方面,这不是避免N + 1问题的好方法。请改用批量大小。它对应用程序完全透明,并通过给定因子减少查询量(从5到50的值是有意义的,如果您不知道要使用什么,请使用10)。

答案 1 :(得分:1)

一个选项,你可以减少选择n + 1问题,保持延迟加载并忘记急切加载....

您需要做的就是向XML添加一个简单属性batch-size

<bag name="ControlDetails" batch-size="25" ..>

我还注意到您的映射中有lazy="true",您是否尝试更改为false?