3

多くの EventTypes を持つことができるイベントがあります。定義した NHibernate マッピングを使用してデータベースにクエリを実行しようとしています。問題は、子オブジェクトの基準を定義するときに、NHibernate が必要なすべてのデータに対して 1 回の呼び出しを行うのではなく、データベースへの呼び出しを繰り返し行うことです。これにより、検索が非常に遅くなります。

データベース テーブルは次のとおりです。

Event
-EventID

EventType
-EventTypeID

EventEventTypeID
-EventEventTypeID
-EventID
-EventTypeID

Event クラスは次のようになります。

Id as Integer
Types as IList(Of EventType)

EventType クラスは次のようになります。

Id as Integer
FullTitle as String

EventEventType クラスはありません。

NHibernate のマッピングは次のとおりです。

イベントマッピング

Table("event.Event")
Id(Function(x) x.Id).Column("EventID").GeneratedBy().Identity()
HasManyToMany(Of EventType)(Function(x) .Types).Table("event.EventEventType").ParentKeyColumn("EventID").ChildKeyColumn("EventTypeID").Cascade.All()

EventTypeMapping

Table("event.EventType")
Id(Function(x) x.Id).Column("EventTypeID").GeneratedBy().Identity()
Map(Function(x) x.FullTitle).Column("EventTypeFullTitle")

フォームを開くと、次の関数が呼び出され、Event の Types プロパティの FetchMode が設定されます。

Public Function CreateListViewQuery(currentNHibernateSession As ISession) As NHibernate.ICriteria Implements IListViewQueryable.CreateListViewQuery

        Return currentNHibernateSession.CreateCriteria(Of [Event])().SetFetchMode("Types", FetchMode.Join)

End Function

これは、すべてのデータに対してデータベースを 1 回呼び出すだけで、非常に迅速に行われるリストビューの作成に使用されます。

SELECT TOP (50)
    this_.EventID as EventID
    , t3_.EventTypeID as EventTyp1_15_0_
    , t3_.EventTypeFullTitle as EventTyp2_15_0_
FROM event.Event this_
    inner join event.EventEventType types5_ on this_.EventID=types5_.EventID 
    inner join event.EventType t3_ on types5_.EventTypeID=t3_.EventTypeID

ただし、次のように Criterion.Expression を追加すると (QuickSearch はユーザー入力):

.CreateAlias("Types", "t", NHibernate.SqlCommand.JoinType.InnerJoin)
.Add(NHibernate.Criterion.Expression.Like("t.FullTitle", QuickSearch))

次のような呼び出しが 1 回あります。

SELECT TOP 50
    this_.EventID as EventID12_6_
    , types8_.EventID as EventID
    , t3_.EventTypeID as EventTyp2_
    , t3_.EventTypeFullTitle as EventTyp2_15_0_ 
FROM
    event.Event this_
        inner join event.EventEventType types8_ on this_.EventID=types8_.EventID
        inner join event.EventType t3_ on types8_.EventTypeID=t3_.EventTypeID
WHERE t3_.EventTypeFullTitle like @p1

そして、次のように見えるさらに 50 の呼び出し (TOP 50 を選択したので 50):

SELECT
    types0_.*
FROM
    event.EventEventType types0_
        left outer join event.EventType eventtype1_ on types0_.EventTypeID=eventtype1_.EventTypeID
WHERE types0_.EventID=@p0

(ここで、@p0 は、検索によって返された上位 50 個のイベントのそれぞれです)

これには最初の呼び出しだけが必要だと思います。

NHibernate がこれらの追加の呼び出しを必要とするのは、多対多の関係の性質ですか? マッピングに見逃したものはありますか?

おそらく重要なのは、Event の String プロパティと Event からの 1 対多の関係に対してまったく同じ手法を使用しており、検索に必要な呼び出しは 1 つだけです。この問題は、多対多の関係でのみ存在するようです。

長い質問で申し訳ありません。ここまでお付き合いいただきありがとうございます。同様のトピックに関する多くの質問を読みましたが、多対多の関係でデータベース呼び出しが繰り返されるという問題に対処するものは見つかりませんでした。

4

1 に答える 1

3

あなたが説明しているのはnHibernate n+1問題です (基本的に、実行されるクエリの数は結果セットのサイズに正比例します)。これは、クエリの複雑さによっては解決が難しい場合があります。

明らかな解決策ではありませんが、過去に私にとってうまくいったのは、以下のように結合タイプを左外部結合に変更することです:

.CreateAlias("Types", "t", NHibernate.SqlCommand.JoinType.LeftOuterJoin)
于 2013-06-27T17:44:30.973 に答える