1

在 NHibernate 中有一个看似奇怪的 N+1 选择问题。我正在执行一个查询,我要求一堆实体,其中一个链接属性为空。在这种情况下,我实际上不需要 NHibernate 返回链接属性,因为它只是为了选择正确的数据。

第一个实体是预订窗口

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>();

所以第一个查询会像这样发送到数据库(选择了订单项列,这有点烦人,但真正的问题在一分钟后出现)

选择 this_.Id 作为 Id2_1_, this_.Price 作为 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_ FROM Customer.BookingWindows this_ 左外连接 Payment.OrderItem bookingwin1_ on this_.Id=bookingwin1_.OrderItemForeignId 和 bookingwin1_.OrderItemTypeId='1' 在哪里 this_.RoomId = ?并且 bookingwin1_.Id 为空

但是对于每个返回的预订窗口,即使我没有要求或不需要它,链接的订单项目也会有一个额外的选择。这发生在查询方法中,因此我不会手动对返回的预订窗口进行任何迭代。

选择 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_。和 bookingwin0_.OrderItemTypeId='1'

谁能向我解释我在这里犯的错误。也许它很明显,但我已经挣扎了几个小时,在我的耐心结束时:)

4

1 回答 1

0

我在您的映射中看到了一个奇怪的部分:References用作一对一的映射样式。也许这是有意的,但这导致了您遇到的问题。

首先,正如文档所说[参考/多对一][1]

引用用于在两个实体之间创建多对一关系,并应用于“多方”。您正在引用单个其他实体,因此您使用 References 方法。#HasMany / 一对多是引用关系的“另一面”,并应用于“一方面”。

换句话说,在BookingWindowOrderItemMap您存储对BookingWindow. 这可能意味着(通过数据库设计),可能有更多的 OrderItem 记录,引用相同的 BookingWindow。但也许这就是你想要的,你在别处检查“唯一性”。我越想了解您的问题,我会投票赞成在 BookingWindow 的列中移动对 OrderItem 的引用

问题暴露:

对你的问题。当 NHibernate 收到 的列表时BookingWindow,下一步是构建代理。在这个过程中,所有的 valueType/string 属性都被设置,并且对于引用...并且对于引用 NHibernate 尝试准备延迟加载。

简化的版本是,在每个属性BookingWindowOrderItem BookingOrderItem中注入一个的实例的承诺BookingWindowOrderItem,在第一次接触时返回。在标准情况下,当使用映射时References,NHibernate 在这一刻已经从 BookingWindow 的表中加载了ReferenceId.

在您的情况下,此 ReferenceID 由虚拟的只读“当前项目 ID”表示。明确存在的ID ...但参考不存在!我们只选择了BookingWindow具有 NULL 而不是引用的 s。

但是我们确实有NOT NULL Reference ID(由 Current instance ID 表示)。

而且我们已经使用了.Left.JoinQueryOver. 所以 NHibernate 可以肯定,它已经在第一个查询中加载了所有数据......但是很困惑,因为在他的会话中没有 ID 等于 BookingWindow.ID/ReferenceId 的 OrderItem

这就是原因(为什么它试图修复它......并再次加载它)

所以这就是答案,为什么 NHibernate 会“奇怪的选择”。不是建议如何解决它;)它可能是另一个问题和答案......

于 2013-05-13T04:49:40.420 回答