NHibernate为什么不急于获取我的数据 [英] Why doesnt NHibernate eager fetch my data

查看:80
本文介绍了NHibernate为什么不急于获取我的数据的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在将Nhibernate用于我的ORM.

I am using Nhibernate for my ORM.

我有一个"Control"类,它与ControlDetail具有一对多的关系(即,一个控件具有许多controlDetails).

I have a class "Control" that has a one to many relationship with ControlDetail (ie. A control has many controlDetails).

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

In the control xml config it has the following

<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>

我相信,除非另行通知,否则它会延迟加载控件的控件详细信息.

such that I believe unless otherwise told it would lazy load the controldetails of a control.

我正在运行NHProf来尝试解决我们遇到的一些性能问题,并且它已针对这些类确定了Select N + 1问题.

I am running NHProf to try and fix some performance issues we are having and it has identfied a Select N + 1 issue around these classes.

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

We are using a repository DA layer and I have tried to see if I can add in a way to eagerly fetch the data when required and came up with this.

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;
}

*注意:我不喜欢存储库的设置方式,但是它是在我进入项目之前完成的,因此我们现在必须坚持使用这种模式.

*Note: I'm not a fan of how the repository is setup but it was done before I came to the project so we have to stick with this pattern for now.

我这样称呼这个方法

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

当我调试GetByID方法并查看retObj时,我可以看到ControlDetails列表已被填充(尽管很奇怪的是,我也注意到没有setfetchmode设置该列表已被填充)

When I debug the GetByID method and look at the retObj I can see that the ControlDetails list has been populated (although strangely enough I also noticed without the setfetchmode set the list was being populated)

即使使用此修复程序,NHProf也会在以下行中标识出Select N + 1问题

Even with this fix NHProf identifies a Select N+1 issue with the following line

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

我到底缺少什么?如何停止此N + 1,但仍要遍历controlDetails列表

What exactly am I missing and how can I stop this N+1 but still iterate over the controlDetails list

xml配置看起来像这样(略作编辑以使其更小)

the xml configs look like so (slightly edited to make smaller)

<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>

推荐答案

渴望获取和渴望加载之间存在实质性差异.取指:将相同的查询放入.它有多种副作用,可能会破坏它.急切的加载意味着迫使NH立即加载它,而不是等到第一次访问它时才加载.仍然会使用其他查询来加载它,这会导致N + 1问题.

There is a substantial difference between eager fetching and eager loading. Fetching means: putting into the same query. It has several side effects and may break it. Eager loading means forcing NH to load it immediately, not waiting until it is accessed the first time. It is still loaded using additional queries, which leads to the N+1 problem.

NH可能不急于获取,因为该属性称为ControlDetails,但是您将ControlDetail作为参数传递.

NH probably does not eagerly fetch because the property is called ControlDetails, but you pass ControlDetail as argument.

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

On the other side, this isn't a good way to avoid the N+1 problem. Use batch-size instead. It is fully transparent to the application and reduces the amount of queries by the given factor (values from 5 to 50 make sense, use 10 if you don't know what to use).

这篇关于NHibernate为什么不急于获取我的数据的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆