web-dev-qa-db-ja.com

n +1の選択を回避するための熱心なフェッチ結合コレクションの基準

アイテムと入札がエンティティであるとしましょう。アイテムには多くの入札があります。それらは、典型的な親子関係でHibernateにマップされます。

<class name="Item" table="ITEM">
  ...
  <set name="bids" inverse="true">
    <key column="ITEM_ID"/>
    <one-to-many class="Bid"/>
  </set>
</class>

このクエリの実行後に各アイテムの入札にアクセスしようとするときに、n + 1の選択を回避するにはどうすればよいですか?

List<Item> items = session.createCriteria(Item.class)
                        .createAlias("bids", "b").
                        .add(Restrictions.gt("b.amount", 100)).
                        .list();

入札には熱心なフェッチが必要ですが、さらにコレクションの制限(b.amount> 100)

私は以下を試しましたが失敗しました:

List<Item> items = session.createCriteria(Item.class)
                        .setFetchMode("bids", FetchMode.JOIN).
                        .createAlias("bids", "b").
                        .add(Restrictions.gt("b.amount", 100)).
                        .list();                        

List<Item> items = session.createCriteria(Item.class)
                        .createCriteria("bids")
                        .add(Restrictions.gt("amount", 100)).
                        .list();                        
12
Camilo Silva

これは、フェッチに参加したコレクションに制限を追加すると、コレクションが初期化されなかった理由の説明です(制限のない同じクエリは、コレクションの熱心なフェッチ):

「テーブルAとBの間に1:nの関係があり、Bに制限を追加して、AとBを熱心にフェッチしたい場合、問題は、AからBにナビゲートしたいときに何が起こるかということです。制限に一致するBのデータのみを表示しますか、それともAに関連するすべてのBを表示する必要がありますか?」 詳細はこちら...

ただし、条件の代わりにHQLを使用して、入札コレクションを部分的にフェッチします

List<Item> items = session.createQuery(
          "from Item i left join fetch i.bids b where b.amount > :amount")
          .setParameter("amount", 100)
          .list();

私には矛盾があるように見えますが、これがどのように機能するかです

ちなみに、必要なのが親とそのすべての子のリストであるが、子がすべて特定の制限を満たしている親だけである場合は、これを使用できます

List<Item> items = session.createQuery(
          "from Item i left join fetch i.bids b " +
          "where not exists (from Bid b where b.item = i and b.amount <= :amount)")
          .setParameter("amount", 100)
          .list();

これは関連する投稿です: Hibernateクエリが完全なオブジェクトを返さない

6
Camilo Silva

この基準クエリは正しいようです:

  List<Item> items = session.createCriteria(Item.class)
                    .setFetchMode("bids", FetchMode.JOIN)
                    .createAlias("bids", "b")
                    .add(Restrictions.gt("b.amount", 100))
                    .list();

FetchMode.JOINは、n+1の問題を解決するためのものです。 default_batch_fetch_sizeを定義しましたか| batch-sizeマッピングまたは構成のどこかに、逆の影響がありますか?

そうでない場合は、HQLの下で試してみて、これで問題が解決することを確認できますか?

 Query query = 
      session.createQuery("from Item it left join it.bids b where b.amount=:bids");
 query.setParamter(bids, 100);
 List<Item> items = query.list();
10
Yogendra Singh

必要なのは、別の基準でサブクエリが存在することを使用する基準だと思います。同様の答えはここにあります: https://stackoverflow.com/a/15768089/1469525

DetachedCriteria criteria = session.createCriteria(Item.class, "i");
criteria.setFetchMode("bids", FetchMode.JOIN);

DetachedCriteria bidCriteria = DetachedCriteria.forClass(Bid.class, "b");
bidCriteria.add(Restrictions.gt("amount", 100));
bidCriteria.add(Restrictions.eqProperty("b.itemId", "i.id"));
criteria.add(Subqueries.exists(bidCriteria.setProjection(Projections.property("b.id"))));
2
Jeff Sheets

このコードを試してください。それは私の問題を解決しました。

List<Item> items = session.createCriteria(Item.class)
                .setFetchMode("bids", FetchMode.SELECT)
                .createAlias("bids", "b")
                .add(Restrictions.gt("b.amount", 100))
                .list();
0