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