我正在使用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>
答案 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?