0

私はこのセットアップを持っています:子のコレクションを持つ親。

class Parent {
    IList<Child> Childs { get; set; }
}

HQL:

("親から").Future();

("子から").Future();

foreach(Parent p in result) {
    foreach(Child c in p.Childs) {
    }
}

これにより、古典的な N+1 問題が得られます。2 つの SQL ステートメントが 1 回の往復でサーバーに送信されるため、すべてのデータが第 1 レベルのキャッシュに存在するため、NH がすべての子に対して SQL をまだ存在させるのはなぜですか。

バージョン 3.1.0.400

4

1 に答える 1

5

future クエリを実行すると、すべての親オブジェクトと子オブジェクトが第 1 レベルのキャッシュに取り込まれます。親オブジェクトには、値を設定する必要がある遅延コレクションが含まれています。コレクションを生成するために、NHibernate はデータベースにクエリを実行する必要があります。(理由についてはすぐに説明します。) クエリは Child オブジェクトを返し、それらの子オブジェクトは既に L1 キャッシュにあります。そのため、これらのオブジェクトを使用してコレクションを作成します。

NHibernate が Childs コレクションに値を設定するためにデータベースにクエリを実行する必要があるのはなぜでしょうか? コレクションに、IsDeleted==true で子オブジェクトを除外する "where" 句を含めることができます。特定の子オブジェクトを除外するコードを EventListener に含めることができます。基本的に、発生する可能性のある多くのことがあり、NHibernate は親オブジェクトと子オブジェクトの関係について何の仮定も立てることができません。

HQL またはマッピングでフェッチ戦略を指定することで、十分な情報を提供できます。HQL では、次のように記述できます。

var parents = session.CreateQuery("from Parent p join fetch p.Childs").Future<Parent>();

親と一緒に子を取得しているため、将来を使用した子オブジェクトクエリは完全にオプションです。結合フェッチのため、重複した Parent オブジェクトが取得されますが、それらは同じオブジェクトになります。(データベースで内部結合を実行し、子行ごとに親行の 1 つのコピーを返します。)parents.Distinct() を反復処理することで、これらを取り除くことができます。

子オブジェクトを対応する親で常にフェッチしたい場合は、親マッピングで fetch="join" を使用することもできます。

<bag name="Children" cascade="all-delete-orphan" fetch="join">
  <key column="ParentId"/>
  <one-to-many class="Child"/>
</bag>

これらのオプションのどちらもシナリオで機能しない場合は、コレクション マッピングでバッチ サイズを指定できます。parent.Childs をヒットするとデータベース クエリが実行されますが、NHibernate は他のコレクション プロキシを積極的に初期化します。

<bag name="Children" cascade="all-delete-orphan" batch-size="10">
  <key column="ParentId"/>
  <one-to-many class="Child"/>
</bag>
于 2011-04-08T23:23:46.117 に答える