2011-04-08 84 views
0

我有這樣的設置:Parent,帶有Child的集合。NHibernate的父級,期貨子集合

class Parent { 
    IList<Child> Childs { get; set; } 
} 

HQL:

( 「從父」)未來();

(「From Child」)。Future();

foreach(Parent p in result) { 
    foreach(Child c in p.Childs) { 
    } 
} 

這給出了經典的N + 1問題。兩條SQL語句以一次往返的方式發送到服務器,所以所有數據都存在於一級緩存中,那麼爲什麼NH對於每個孩子仍然存在SQL。

版本3.1.0.400

回答

5

當你執行今後的查詢,你拉的所有父母與子女對象爲1級高速緩存。父對象包含需要填充的惰性集合。爲了填充集合,NHibernate必須查詢數據庫。 (我們將在短短一秒鐘內找到原因。)查詢返回Child對象,這些子對象已經在L1緩存中。所以這些對象被用來填充集合。

現在爲什麼NHibernate必須查詢數據庫來填充Childs集合?你可以在集合上有一個「where」子句,用IsDeleted == true過濾出Child對象。您可以在EventListener中的代碼中篩選出某些Child對象。基本上可以發生很多事情,NHibernate不能對Parent和Child對象之間的關係做任何假設。

您可以通過在HQL或映射中指定獲取策略來爲其提供足夠的信息。在HQL中,您可以編寫:

var parents = session.CreateQuery("from Parent p join fetch p.Childs").Future<Parent>(); 

使用未來的子對象查詢將是完全可選的,因爲您正在使用父項獲取子項。由於聯合提取,您將獲得重複的父對象,儘管它們將是同一個對象。 (你正在數據庫中做一個內部連接,併爲每個子行返回一個父行的副本。)你可以通過在parents.Distinct()上迭代來擺脫這些。

如果您始終想要使用相應的Parent獲取子對象,則還可以在您的父映射中使用fetch =「join」。

<bag name="Children" cascade="all-delete-orphan" fetch="join"> 
    <key column="ParentId"/> 
    <one-to-many class="Child"/> 
</bag> 

如果這些選項都不適用於您的方案,則可以在集合映射上指定批處理大小。當你點擊parent.Child時,你仍然會執行一個數據庫查詢,但是NHibernate會急切地初始化任何其他集合代理。

<bag name="Children" cascade="all-delete-orphan" batch-size="10"> 
    <key column="ParentId"/> 
    <one-to-many class="Child"/> 
</bag>