1

在NHibernate中有一個看起來很棒的N + 1選擇問題。我正在執行一個查詢,在那裏我要求一組實體,其中一個鏈接的屬性爲null。在本例中,我並不需要將NHibernate作爲唯一的鏈接屬性返回,以便選擇正確的數據。導致N + 1的NHibernate QueryOver.List

第一實體是預訂窗口

public class BookingWindow : Entity<BookingWindow> 
{ 
    // Blah blah blah 

    /// <summary> 
    /// Gets or sets the booking order item. 
    /// </summary> 
    /// <value> 
    /// The booking order item. 
    /// </value> 
    public virtual BookingWindowOrderItem BookingOrderItem { get; set; } 
} 

而且BookingWindowOrderItem如下

public class BookingWindowOrderItem : OrderItem 
{ 
    // Blah blah blah 

    public virtual BookingWindow BookingWindow { get; set; } 
} 

這裏是各自映射

public BookingWindowMap() 
    { 
     this.Schema("Customer"); 
     this.Table("BookingWindows"); 
     this.Id(x => x.Id).GeneratedBy.Guid(); 
     this.Component(x => x.WindowPeriod, m => 
     { 
      m.Map(x => x.Min, "StartTime"); 
      m.Map(x => x.Max, "EndTime"); 
     }); 

     this.References(window => window.BookingOrderItem).PropertyRef("BookingWindow").Column("Id").LazyLoad().Nullable().ReadOnly(); 
     this.Map(x => x.Price); 
     this.References(x => x.CustomerRoom).ForeignKey("RoomId").Column("RoomId"); 
    } 

而且

public BookingWindowOrderItemMap() 
    { 
     this.DiscriminatorValue(1); 
     this.References(x => x.BookingWindow).LazyLoad().Column("OrderItemForeignId").ForeignKey("OrderItemForeignId"); 
    } 

現在,當我執行以下查詢時,我找回沒有訂單項目的正確預訂窗口。

Session.QueryOver<BookingWindow>().Where(w => w.CustomerRoom.Id == Guid.Parse(roomId)).Left.JoinQueryOver(bw => bw.BookingOrderItem).WhereRestrictionOn(item => item.Id).IsNull.List<BookingWindow>(); 

所以第一個查詢被髮給數據庫,像這樣(被選中的訂單項目列這是一個有點討厭,但真正的問題是在一分鐘內)

SELECT this_.Id爲Id2_1_, this_.Rrice爲Price2_1_,this_.RoomId爲RoomId2_1_,this_.StartTime爲StartTime2_1_,this_.EndTime爲EndTime2_1_,bookingwin1_.Id爲Id4_0_,bookingwin1_.Price爲Price4_0_,bookingwin1_.Description爲Descript4_4_0_,bookingwin1_.OrderId爲OrderId4_0_,bookingwin1_。 OrderItemParentId爲OrderIte6_4_0_,bookingwin1_.OrderItemForeignId爲OrderIte7_4_0_從Customer.BookingWindows this_左外部聯接Payment.OrderItem bookingwin1_ on this_.Id = bookingwin1_.OrderItemForeignI d和bookingwin1_.OrderItemTypeId ='1'where_.RoomId =?並且bookingwin1_.Id爲空

但是,對於每個預訂窗口返回的鏈接訂單項目都有額外的選擇,即使我沒有要求或需要它。這發生在查詢方法內,所以我沒有做任何手動迭代返回的預訂窗口。

SELECT bookingwin0_.Id如Id4_0_,bookingwin0_.Price如Price4_0_,bookingwin0_.Description如Descript4_4_0_,bookingwin0_.OrderId如OrderId4_0_,bookingwin0_.OrderItemParentId如OrderIte6_4_0_,bookingwin0_.OrderItemForeignId如OrderIte7_4_0_ FROM Payment.OrderItem bookingwin0_ WHERE bookingwin0_.OrderItemForeignId = ?和bookingwin0_.OrderItemTypeId ='1'

任何人都可以向我解釋我在這裏所犯的錯誤。也許它很明顯,但我已經掙扎了幾個小時,並在我的耐心結束:)

回答

0

我看到你的映射中的一個奇怪的部分:使用References作爲一對一的映射風格。也許它是有意的,但是這導致了你的問題。

首先,作爲文檔說【參考文獻/多到一個] [1]

參考文獻是用於建立兩個 實體之間的許多對一的關係,以及施加在「多側「。您引用的是單個其他實體,因此您使用References方法。 #HasMany/ 一對多是參考關係的「另一面」,並且 會在「一側」應用。「

換句話說,在表中的BookingWindowOrderItemMap你存儲參考BookingWindow。這可能意味着(由DB設計),有可能有OrderItem的多個記錄,引用相同BookingWindow。但也許這是你想要什麼,並選中「唯一性」在其他地方我越試圖理解你的問題,我會投票給一列移動參考OrderItem的在BookingWindow

問題透露:

要你的問題。當NHibernate收到的列表210,下一步是建立一個代理。在這個過程中,所有的valueType /字符串屬性都被設置,並且對於引用...而對於NHibernate嘗試準備延遲加載的引用。

簡化版本,即進入每個屬性BookingWindowOrderItem BookingOrderItem注入一承諾BookingWindowOrderItem的實例,當首先觸及歸還。在標準情況下,當使用映射References時,NHibernate在這一刻已經從BookingWindow的表ReferenceId加載。

對於您的情況,此參考ID由虛擬只讀'當前項目ID'表示。定義爲的ID存在 ...但是參考不是!我們只選擇了BookingWindow,它具有NULL而不是參考。

但我們確實有非NULL引用ID(由當前實例ID表示)。我們使用.Left.JoinQueryOver。所以NHibernate可以肯定,它已經加載了第一個查詢中的所有數據......但是很混亂,因爲在他的會話中沒有OrderItem,其ID等於BookingWindow.ID/ReferenceId

這就是爲什麼它會嘗試修復它...並且再次加載它)

所以這就是答案,爲什麼NHibernate會做「怪異的選擇」。不是一個建議如何解決它;)它可能是另一個問題和答案...

+0

感謝您回到這一個。我現在可以理解這是如何引起你的解釋,並決定作出調整,以阻止這種情況發生。 – 2013-05-18 14:24:19

+0

偉大的在這裏。希望NHibernate的最佳...學習有點具有挑戰性,但是一旦你知道如何去管理它,那就太好了。 – 2013-05-18 14:29:00