7

私はこのオブジェクトグラフを持っています:

// Lots of stuff omitted for brevity; these are all virtual properties and there
// are other properties which aren't shown on all classes.
class A {
    B b;
    C c;
    DateTime timestamp;
}
class B {
    X x;
    Y y;
}
class X {
    int id;
}
class C { }
class Y { }

or to put it more simply,
a = {
   b: {
      x { id: int },
      y: { }
   },
   c: { },
   timestamp: DateTime
}      

今、私は s のリストを返すクエリを作成しており、AすべてのBs、Cs、Xs、およびYs が必要です。また、それらを B でグループ化してルックアップします。

ILookup<B, A> GetData(List<int> ids) {
    using (ISession session = OpenSession()) {
        var query = from a in session.Query<A>()
                    where ids.Contains(a.b.x.id)
                    orderby A.timestamp descending
                    select a;

        query = query
            .Fetch(a => a.b)
            .ThenFetch(b => b.x)
            .Fetch(a => a.b)
            .ThenFetch(b => b.y)
            .Fetch(a => a.c);

       return query.ToLookup(a => a.b);
   }
}

注意すべき点がいくつかあります。

  1. これは、すべてのデータを返す必要があるレポートです。無制限の結果は問題ではありません。
  2. すべての実際の値が必要な場合、使用はより複雑に見えるToLookupため、使用してグループ化を行っています。グループについてデータベースにクエリを実行し、次にそれらの実際の値についてクエリを実行する必要があります。group by

私の質問は、フェッチ戦略を適切に指定する方法です。私が行った方法は、これを実行するために見つけた唯一の方法です(すべてのbxとby値をフェッチしました)-しかし、間違っているように見えるSQLを生成します:

select  /* snipped - every mapped field from a0, b1, x2, b3, y4, c5 - but not b6 */
from     [A] a0
         left outer join [B] b1
           on a0.B_id = b1.BId
         left outer join [X] x2
           on b1.X_id = x2.XId
         left outer join [B] b3
           on a0.B_id = b3.BId
         left outer join [Y] y4
           on b3.Y_id = y4.YId
         left outer join [C] c5
           on a0.C_id = c5.CId,
         [B] b6
where    a0.B_id = b6.BId
         and (b6.X_id in (1, 2, 3, 4, 5))
order by a0.timestamp desc

ご覧のとおり、値をa.b3 回取得b1しています。b3フェッチとb6where 句です。

  1. これは DB のパフォーマンスに悪影響を与えると思いますが、正しいでしょうか?
  2. .Fetch一度だけ取得するように呼び出しを変更する方法はありa.bますか?
  3. これは私の問題に対する良いアプローチですか?
4

1 に答える 1

6

1 つのクエリで 1 対多のプロパティを複数回フェッチすると、デカルト積が得られます。NHibernate はこれを処理しません - 私の知る限り、実際の SQL 結合のように動作させるために意図的に行われました。HQL も同じことを行います。

一度にすべてのフェッチを行う必要はありません。クエリを分割し、1 対多の各フェッチ/結合を別のクエリで実行します。それぞれがセッション内のデータをキャッシュし、すべてのオブジェクト参照を適切に接続します。(注: LINQ でこれを試したことはありませんが、HQL でも機能し、原則は同じです)

頭のてっぺんから、次のようになります。

ILookup<B, A> GetData(List<int> ids) {
using (ISession session = OpenSession()) {
    var query = from a in session.Query<A>()
                where ids.Contains(a.b.x.id)
                orderby A.timestamp descending
                select a;

    query
        .Fetch(a => a.b)
        .ThenFetch(b => b.x)
        .ToList();
    query
        .Fetch(a => a.b)
        .ThenFetch(b => b.y)
        .Fetch(a => a.c)
        .ToList();

   return query.ToLookup(a => a.b);
}

ToList() の代わりに ToFuture() メソッドを使用して、さらに最適化を行うことができます... LINQ および ToLookup メソッドでどのように機能するかはわかりませんが、正しく理解するのはそれほど難しくありません。ToFuture() はクエリをキューに入れ、クエリごとに個別のデータベース接続を行うのではなく、1 つの SQL コマンドとして実行します。

于 2011-01-07T11:16:08.913 に答える