it-swarm.com.de

Kriterien, die gerne zusammengestellt wurden, um n + 1 Auswahlen zu vermeiden

Nehmen wir an, Artikel und Gebote sind Entitäten: Ein Artikel hat viele Gebote. Sie werden in Hibernate in einer typischen Eltern/Kind-Beziehung abgebildet:

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

Wie kann ich vermeiden, dass n + 1 ausgewählt wird, wenn versucht wird, auf die Gebote jedes Artikels zuzugreifen, nachdem diese Abfrage ausgeführt wurde?

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

Hinweis Ich benötige ein eifriges Abrufen für Gebote, aber mit einer weiteren Einschränkung der Sammlung (b.amount> 100)

Ich habe folgendes erfolglos versucht:

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();                        
11
Camilo Silva

Dies ist eine Erklärung dafür, warum das Hinzufügen einer Einschränkung für die fetch-join-Collection dazu führt, dass die Collection nicht initialisiert wurde ( note dass dieselbe Abfrage ohne Einschränkung einen eifrigen Abruf für die Collection erzeugt):

"Wenn Sie eine 1: n - Beziehung zwischen den Tabellen A und B haben und Sie eine Einschränkung für B hinzufügen und A und B mit Eifer abrufen möchten, lautet die Frage, was passiert, wenn Sie von A nach B navigieren möchten. ____.] Sollten Sie nur die Daten in B sehen, die der Einschränkung entsprechen, oder sollten Sie alle mit A verbundenen Bs sehen? " mehr hier ...

Verwenden Sie jedoch HQL anstelle von Kriterien, und holen Sie die Gebotsgruppen teilweise ab.

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

Es scheint mir eine Inkonsistenz, aber so funktioniert das

Übrigens, wenn Sie brauchen, ist die Liste der Eltern und aller ihrer Kinder, aber nur die Eltern, deren Kinder alle bestimmten Einschränkungen unterliegen, so dass Sie diese verwenden können

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();

Dies ist ein verwandter Beitrag: Hibernate-Abfrage, die kein vollständiges Objekt zurückgibt .

6
Camilo Silva

Diese Kriterienabfrage scheint richtig zu sein:

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

FetchMode.JOIN soll n+1-Problem lösen. Haben Sie default_batch_fetch_size | definiert batch-size überall im Mapping oder in der Konfiguration, was sich umgekehrt auswirkt?

Wenn nicht, können Sie es unter HQL versuchen und sehen, dass dies Ihr Problem löst?

 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();
9
Yogendra Singh

Ich denke, was Sie brauchen, ist ein Kriterium, das Subquery Exists für andere Kriterien verwendet. Eine ähnliche Antwort ist hier: 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"))));
1
Jeff Sheets

Versuchen Sie diesen Code. Es hat mein Problem gelöst.

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