2011-08-03 59 views
2

我對我的ORM使用Nhibernate。我有一個「控制」類與ControlDetail有一對多的關係(即一個控件有很多controlDetails)。爲什麼NHibernate非常渴望獲取我的數據

在控制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> 

這樣的,我相信除非另有告訴它會延遲加載控件的controldetails。

我正在運行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標識與下面的行

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

什麼exactl一個選擇N + 1問題Ÿ我錯過了,我怎樣才能制止這種N + 1,但仍然在controlDetails列表迭代

編輯:XML CONFIGS看起來像這樣(略編輯來讓更小)

<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

'Control'最有可能來自您的第一級緩存,但關係正在延遲加載,因爲關係未設置爲與父級緩存。抓取NHProf並查看緩存匹配與查詢。 – Phill

+0

nhprof在查詢緩存命中/未命中/放置計數中顯示0,在二級緩存命中/未命中/放置計數中顯示0,我將如何解決這個問題? –

+0

這是0命中控制和ControlDetail?或者只是ControlDetail? – Phill

回答

5

渴望獲取和渴望加載之間存在很大差異。提取意味着:放入相同的查詢。它有幾個副作用,可能會打破它。急切的加載意味着迫使NH立即加載它,而不是等到它第一次被訪問。它仍然使用附加查詢加載,導致N + 1問題。

NH可能不會急於獲取,因爲該屬性名爲ControlDetails,但您傳遞的參數爲ControlDetail

另一方面,這不是避免N + 1問題的好方法。改用批量大小。它對應用程序是完全透明的,並通過給定的因子減少查詢量(5到50的值是有意義的,如果你不知道使用什麼,則使用10)。

+1

+ 1爲渴望獲取的副作用,尤其是在尋呼... – mathieu

+1

@mathieu:的確如此。並嘗試在相同的查詢中嘗試多取一個集合。有一個DistinctRootEntityTransformer,但沒有「DistinctCollectionItemTransformer」。一切都會增加。我在查詢中遇到了太多列的奇怪問題。我真的停止使用它了。 –

1

一種選擇,可以降低選擇N + 1點的問題,不斷延遲加載,而忘記了預先加載....

所有你需要做的是添加上Ë簡單屬性batch-size將XML

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

我還注意到,您在您的映射有lazy="true",你有沒有嘗試更改爲假?