Criteria API: выборка списка повторяет основной объект

У меня есть следующие сущности; Билет содержит набор 0, N WorkOrder:

@Entity
public class Ticket {

  ...

  @OneToMany(mappedBy="ticket", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
  private List<WorkOrder> workOrders = null;

  ...
}

@Entity
public class WorkOrder {
  ...
  @ManyToOne
  @JoinColumn(nullable = false)
  private Ticket ticket;
}

Я загружаю билеты и выбираю атрибуты. Все атрибуты 0,1 не представляют проблемы. Для заказа работ я использовалэтот ответ чтобы получить следующий код.

CriteriaBuilder criteriaBuilder = this.entityManager.getCriteriaBuilder();
CriteriaQuery<Ticket> criteriaQuery = criteriaBuilder
  .createQuery(Ticket.class);
Root<Ticket> rootTicket = criteriaQuery.from(Ticket.class);

ListAttribute<? super Ticket, WorkOrder> workOrders =
  rootTicket.getModel().getList("workOrders", WorkOrder.class);
rootTicket.fetch(workOrders, JoinType.LEFT);

    // WHERE logic
    ...

criteriaQuery.select(rootTicket);
TypedQuery<Ticket> query = this.entityManager.createQuery(criteriaQuery);
return query.getResultList();

В результате в запросе, который должен вернуть мне 1 билет с 5 заказами, я получаю один и тот же билет 5 раз.

Если я просто сделаю workOrders Eager Fetch и удаляю код выборки, он работает как надо.

Может кто-нибудь мне помочь? Заранее спасибо.

ОБНОВИТЬ:

Одно объяснение того, почему я не просто доволен ответом Дж. Б. Низета (даже если в конце это сработает).

Когда я просто стремлюсь к отношениям, JPA проверяет те же самые данные, что и когда я делаю их ленивыми и добавляю предложение fetch в Criteria / JPQL. Отношения между различными элементами также ясны, так как я определяюListAttribute для запроса критериев.

Есть какое-то разумное объяснение, почему JPA не возвращает одинаковые данные в обоих случаях?

ОБНОВЛЕНИЕ ДЛЯ BOUNTY. Хотя ответ JB Nizet действительно решил проблему, я все же считаю бессмысленным, что, учитывая две операции с одинаковым значением (& quot; ПолучитьTicket и получить всеWorkOrder внутриticket.workOrders& quot;), выполнение их энергичной загрузкой не требует дальнейших изменений, в то время как указание выборки требуетDISTINCT команда

 JMelnik14 июн. 2012 г., 14:16
Если вы посмотрите наLeft JoinТак оно и есть. Зачем вам нужно получать результаты по левому входу?
 JB Nizet26 июн. 2012 г., 11:35
Смотрите мой отредактированный ответ.
 SJuan7614 июн. 2012 г., 15:02
Из трех вариантов, доступных в API критериев, он является более разумным. Мы говорим здесь о JPA, поэтому я ожидал, что API организует SQL в сущностях более правильным образом.

Ответы на вопрос(2)

Решение Вопроса

ing doesn't mean that the data is loaded within the same query. It just means that it is loaded immediately, although by additional queries.

The criteria is always translated to an SQL query. If you specify joins, it will be join in SQL. By the nature of SQL, this multiplies the data of the root entity as well, which leads to the effect you got. (Note that you get the same instance multiple times, so the root entity is not multiplied in memory.)

Есть несколько решений для этого:

use distinct(true) Use the distinct root entity transformer (.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY)). When you don't need to filter by child properties, avoid the join When you need to filter by child properties, filter by a subquery (DetachedCriteria). Optimize the N+1 problem by using batch-size

distinct(true) на CriteriaQuery?

Спецификация JPA 2, стр. 161, гласит:

The DISTINCT keyword is used to specify that duplicate values must be eliminated from the query result.

If DISTINCT is not specified, duplicate values are not eliminated.

Javadoc также говорит:

Specify whether duplicate query results will be eliminated.A true value will cause duplicates to be eliminated. A false value will cause duplicates to be retained. If distinct has not been specified, duplicate results must be retained.

Причина, по которой вам не нужно различение, когда ассоциативно загружена ассоциация, заключается, вероятно, в том, что ассоциация загружается не с использованием выборочного соединения, а с использованием дополнительного запроса.

 14 июн. 2012 г., 13:28
Я полагаю, что это правильное решение, потому что именно это и делает ключевое слово в JPQL (или, по крайней мере, в HQL).
 07 сент. 2015 г., 16:18
Единственная проблема с distrinct (true) состоит в том, что он портит ваш OrderBy, если они основаны на атрибуте атрибута.
 SJuan7614 июн. 2012 г., 13:25
Хорошо, сdistinct(true) оно работает. Но так ли это "правильно"? решение или просто обходной путь? Я считаю нелогичным, что я говорю JPA, что я просто ищуTicket и что WorkOrders должен быть загружен в список, и что JPA возвращает мне только декартово произведение.

Ваш ответ на вопрос