32

私は数学が苦手ですが、デカルト積が何であるかはなんとなくわかります。
これが私の状況です(簡略化):

public class Project{
 public IList<Partner> Partners{get;set;}
}
public class Partner{
 public IList<PartnerCosts> Costs{get;set;}
 public IList<Address> Addresses{get;set;}
}
public class PartnerCosts{
 public Money Total{get;set;}
}
public class Money{
 public decimal Amount{get;set;}
 public int CurrencyCode{get;set;}
}
public class Address{
 public string Street{get;set;}
}

私の目的は、プロジェクト全体を効果的にロードすることです。

もちろん問題は次のとおりです。

  • パートナーとそのコストを熱心に読み込もうとすると、クエリは膨大な数の行を返します
  • Partner.Costs を遅延ロードすると、db はリクエスト スパムを取得します (これは最初のアプローチよりも少し高速です)。

私が読んだように、一般的な回避策は MultiQueries を使用することですが、ちょっとわかりません。
ですから、この正確な例を通して学びたいと思っています。

プロジェクト全体を効果的にロードするには?

Ps 私は NHibernate 3.0.0 を使用しています。
hql または文字列形式の基準 API アプローチで回答を投稿しないでください。

4

4 に答える 4

47

わかりました、私はあなたの構造を反映した自分自身の例を書きましたが、これはうまくいくはずです:

int projectId = 1; // replace that with the id you want
// required for the joins in QueryOver
Project pAlias = null;
Partner paAlias = null;
PartnerCosts pcAlias = null;
Address aAlias = null;
Money mAlias = null;

// Query to load the desired project and nothing else    
var projects = repo.Session.QueryOver<Project>(() => pAlias)
    .Where(p => p.Id == projectId)
    .Future<Project>();

// Query to load the Partners with the Costs (and the Money)
var partners = repo.Session.QueryOver<Partner>(() => paAlias)
    .JoinAlias(p => p.Project, () => pAlias)
    .Left.JoinAlias(() => paAlias.Costs, () => pcAlias)
    .JoinAlias(() => pcAlias.Money, () => mAlias)
    .Where(() => pAlias.Id == projectId)
    .Future<Partner>();

// Query to load the Partners with the Addresses
var partners2 = repo.Session.QueryOver<Partner>(() => paAlias)
    .JoinAlias(o => o.Project, () => pAlias)
    .Left.JoinAlias(() => paAlias.Addresses, () => aAlias)
    .Where(() => pAlias.Id == projectId)
    .Future<Partner>();

// when this is executed, the three queries are executed in one roundtrip
var list = projects.ToList();
Project project = list.FirstOrDefault();

私のクラスは名前が異なっていましたが、まったく同じ構造を反映していました。名前を置き換えました。タイプミスがないことを願っています。

説明:

結合には別名が必要です。Project必要なをロードする 3 つのクエリを定義PartnersしましCostsた。. _ _PartnersAddresses.Futures()projects.ToList()

これにより、1 回のラウンドトリップで実際に実行される 3 つの SQL ステートメントが生成されます。3 つのステートメントは、次の結果を返します。1) プロジェクトの 1 行 2) パートナーとそのコスト (および金額) の x 行 (x はプロジェクトのパートナーのコストの合計数) 3) プロジェクトのパートナーの y 行パートナーとその住所。ここで、y はプロジェクトのパートナーの住所の総数です。

あなたのデータベースは、デカルト積である x*y 行ではなく、1+x+y 行を返す必要があります。あなたのDBが実際にその機能をサポートしていることを願っています。

于 2011-03-12T21:50:58.590 に答える
5

NHibernate で Linq を使用している場合は、次のようにしてデカルト防止を簡素化できます。

int projectId = 1;
var p1 = sess.Query<Project>().Where(x => x.ProjectId == projectId);


p1.FetchMany(x => x.Partners).ToFuture();

sess.Query<Partner>()
.Where(x => x.Project.ProjectId == projectId)
.FetchMany(x => x.Costs)
    .ThenFetch(x => x.Total)
.ToFuture();

sess.Query<Partner>()
.Where(x => x.Project.ProjectId == projectId)
.FetchMany(x => x.Addresses)
.ToFuture();


Project p = p1.ToFuture().Single();

ここでの詳細な説明: http://www.ienablemuch.com/2012/08/solving-nhibernate-thenfetchmany.html

于 2012-08-25T11:00:28.363 に答える
2

複数のコレクションを熱心に取得して厄介なデカルト積を取得する代わりに、次のようにします。

Person expectedPerson = session.Query<Person>()
    .FetchMany(p => p.Phones)
        .ThenFetch(p => p.PhoneType)
    .FetchMany(p => p.Addresses)
    .Where(x => x.Id == person.Id)
    .ToList().First();

1 回のデータベース呼び出しで子オブジェクトをバッチ処理する必要があります。

// create the first query
var query = session.Query<Person>()
      .Where(x => x.Id == person.Id);
// batch the collections
query
   .FetchMany(x => x.Addresses)
   .ToFuture();
query
   .FetchMany(x => x.Phones)
   .ThenFetch(p => p.PhoneType)
   .ToFuture();
// execute the queries in one roundtrip
Person expectedPerson = query.ToFuture().ToList().First();

Linq、QueryOver、または HQL http://blog.raffaeu.com/archive/2014/07/04/nhibernate-fetch-strategies/を使用してそれを回避する方法を説明するブログ投稿を書きました。

于 2014-07-04T05:48:47.830 に答える