Критерии для сбора собранных с помощью выборки, чтобы избежать выбора 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"))));