一般的に、NHibernateはキャッシュを使用して、Multiqueryを介して実行されたクエリの結果を「組み合わせる」ことができます。ただし、これは通常、遅延コレクションが制限なしでロードされる場合にのみ適用されることに注意してください。
例:
Invoice iAlias = null;
InvoiceDetails idAlias = null;
// Base-Query: get Invoices with certain condition
var invoices = session.QueryOver<Invoice>()
.Where(i => i.Number == "001")
.Future<Invoice>();
// Option 1: this will still cause N+1 if we iterate through invoices,
// because it doesn't know better
var invoicedetails = session.QueryOver<InvoiceDetails>()
.JoinAlias(a => a.Invoice, () => iAlias)
.Where(() => iAlias.Number == "001")
.Future<InvoiceDetails>();
// Option 2: this will still cause N+1 if we iterate through invoices,
// because we limited the possible results using a where-condition
var invoices2 = session.QueryOver<Invoice>()
.Left.JoinAlias(i => i.Details, () => idAlias)
.Where(i => i.Number == "001")
.And(() => idAlias.Quantity > 5)
.Future<Invoice>();
// Option 3: this will work without N+1, because we don't use a filter
// -> NHibernate will use the collection in cache
var invoices3 = session.QueryOver<Invoice>()
.Left.JoinAlias(i => i.Details, () => idAlias)
.Where(i => i.Number == "001")
.Future<Invoice>();
foreach (Invoice i in invoices)
{
int count = i.Details.Count;
}
3つのオプションのうち2つをコメントアウトしてコードを実行すると、オプション3のみがN + 1を防止し、他の2つはループ内のInvoiceDetails
それぞれのforをロードすることがわかります。Invoice
もちろん、これは非常に単純な例であり、オプション3をBaseクエリなしで実行しても同じ結果が返されることは明らかですが、理解していただければ幸いです。
2つの異なるエンティティのセットをロードする場合、つまりオプション1のようにルートクラスが異なる場合、この「結合」はほとんど機能しません。
申し訳ありませんが、HQLの代わりにQueryOverを使用した場合、同じルールが適用されます。