1

NHibernate で奇妙な N+1 選択の問題が発生しています。リンクされたプロパティの 1 つが null である一連のエンティティを要求するクエリを実行しています。この場合、正しいデータを選択するためだけに 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_ として選択します。 OrderId4_0_ としての .OrderId、Bookingwin1_.OrderItemParentId として OrderIte6_4_0_、bookingwin1_.OrderItemForeignId として OrderIte7_4_0_ FROM Customer.BookingWindows this_ left 外部結合 Payment.OrderItem bookingwin1_ on this_.Id=bookingwin1_.OrderItemForeignId および bookingwin1_.OrderItemTypeId='1' WHERE this_.RoomId = ? そしてbookingwin1_.Idはnullです

しかし、返された予約ウィンドウごとに、リンクされた注文項目を要求していないか、必要としていないにもかかわらず、追加の選択があります。これはクエリ オーバー メソッド内で発生するため、返された予約ウィンドウを手動で反復処理することはありません。

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_ として選択します。そしてbookingwin0_.OrderItemTypeId='1'

ここで私が犯したエラーを誰かに説明してもらえますか。たぶんそれは明らかですが、私は数時間苦労し、忍耐力の終わりに:)

4

1 に答える 1

0

あなたのマッピングには奇妙な部分が 1 つありますReferences。1 対 1 のマッピング スタイルとして使用しています。意図したものかもしれませんが、これが問題の原因です。

まず、ドキュメントにあるように [参照 / 多対一][1]

参照は、2 つのエンティティ間に多対 1 の関係を作成するためのもので、「多面」に適用されます。他の 1 つのエンティティを参照しているので、References メソッドを使用します。#HasMany / 1 対多は参照関係の「反対側」であり、「一方」に適用されます。

つまり、BookingWindowOrderItemMapあなたのテーブルには への参照が保存されBookingWindowます。同じ BookingWindow を参照する OrderItem のレコードがさらに存在する可能性があることを (DB 設計によって) 意味する可能性があります。しかし、おそらくこれはあなたが望むものであり、他の場所で「一意性」をチェックします。あなたの問題を理解しようとすればするほど、BookingWindowの列にあるOrderItemへの参照を移動することに投票します

明らかになった問題:

あなたの問題に。NHibernate が のリストを受け取るとBookingWindow、次のステップはプロキシを構築することです。このプロセスでは、すべての valueType/string プロパティが設定され、参照用に...そして参照用に NHibernate は遅延読み込みを準備しようとします。

簡略化されたバージョンは、各プロパティに のインスタンスのpromiseBookingWindowOrderItem BookingOrderItemが注入され、最初に触れたときに返されることです。標準的なケースでは、マッピングが使用される場合、NHibernate はこの時点ですでに BookingWindow のテーブルからロードされています。BookingWindowOrderItemReferencesReferenceId

あなたの場合、この ReferenceID は、仮想の読み取り専用の「現在のアイテム ID」で表されます。明確に存在するID ... しかし、参照は存在しません! BookingWindow参照の代わりに NULL を持つのみを選択しました。

ただし、NOT NULL参照 ID (現在のインスタンス ID で表される) はありません。

を使用しまし.Left.JoinQueryOverた。したがって、NHibernate は、最初のクエリですべてのデータを既に読み込んでいると確信しています...しかし、彼のセッションには、BookingWindow.ID/ReferenceId と等しい ID を持つ OrderItem がないため、混乱しています。

それが理由です(なぜそれを修正しようとするのか...そして再度ロードします)

これが答えです。NHibernate が「奇妙な選択」を行う理由です。修正方法の提案ではありません;)別の質問と回答になる可能性があります...

于 2013-05-13T04:49:40.420 に答える