web-dev-qa-db-fra.com

Critères de collecte fetch-join pour éviter n + 1 sélections

Disons que l'article et l'offre sont des entités: un article a plusieurs offres. Ils sont mappés dans Hibernate dans une relation typique parent/enfant:

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

Comment éviter les n + 1 sélections lors de la tentative d'accès aux offres de chaque élément une fois la requête exécutée?

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

Remarque J'ai besoin d'un recherche anticipée pour les offres, mais avec une restriction supplémentaire sur la collecte (b.amount> 100)

J'ai essayé ce qui suit sans succès:

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

Ceci explique pourquoi l'ajout d'une restriction à la collection fetch-join a pour conséquence que la collection n'a pas été initialisée ( note si la même requête sans restriction produit une extraction enthousiaste pour la collection):

"Si vous avez une relation 1: n entre les tables A et B, que vous ajoutez une restriction à B et que vous voulez aller chercher A et B avec impatience, la question serait de savoir ce qui se passe lorsque vous voulez naviguer de A à B. Ne devriez-vous voir que les données de B qui correspondent à la restriction, ou devriez-vous voir tous les Bs liés à A? " voir plus ici ...

Cependant, en utilisant HQL au lieu de critères, récupérez partiellement les collections d'enchères

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

_ {Cela me semble une incohérence, mais c'est comme ça que ça marche}

Soit dit en passant, si vous avez besoin de la liste des parents et de tous ses enfants, mais uniquement des parents dont les enfants rencontrent tous certaines restrictions, vous pouvez donc utiliser cette liste.

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

Ceci est une publication connexe: La requête Hibernate ne renvoie pas d’objet complet .

6
Camilo Silva

Cette requête de critère semble juste:

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

FetchMode.JOIN est destiné à résoudre le problème n+1. Avez-vous défini certains default_batch_fetch_size | batch-size n'importe où dans le mappage ou la configuration, ce qui a un impact inverse?

Si non, pouvez-vous s'il vous plaît essayer ci-dessous HQL et voir cela résout votre problème?

 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

Je pense que ce dont vous avez besoin est un critère qui utilise la sous-requête existante sur un autre critère. Une réponse similaire se trouve ici: 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

Essayez ce code. C'est résolu mon problème.

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