Критерии для сбора собранных с помощью выборки, чтобы избежать выбора n + 1

Предположим, что Item и Bid являются объектами: Item имеет много ставок. Они отображаются в 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();                        

Ответ 1

Это объяснение того, почему добавление ограничения на сборку, состоящее из выборки, приводит к тому, что коллекция не была инициализирована (note), что один и тот же запрос без ограничения создает ожидающую выборку для коллекции):

"Если у вас есть соотношение 1: n между таблицами A и B, и вы добавляете ограничение на B и хотите получить A и B с нетерпением, вопрос будет таким, что будет, когда вы хотите перейти от A к B, Если вы видите только данные в B, которые соответствуют ограничению, или вы должны увидеть все B, связанные с A?" см. Подробнее здесь...

Однако, используя 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 не возвращает полный объект.

Ответ 2

Этот запрос критериев кажется правильным:

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

Ответ 3

Я думаю, что вам нужен критерий, который использует Subquery Exists для другого критерия. Аналогичный ответ можно найти здесь: fooobar.com/info/534081/...

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