Подтвердить что ты не робот

Отношение "один ко многим" позволяет получить дубликаты объектов без использования "различных". Зачем?

У меня есть 2 класса в отношениях "один ко многим" и запрос HQL, который немного странный. Даже если я прочитал некоторые уже опубликованные вопросы, мне это пока не кажется.

Class Department{
   @OneToMany(fetch=FetchType.EAGER, mappedBy="department")
   Set<Employee> employees;
}
Class Employee{
   @ManyToOne
   @JoinColumn(name="id_department")
   Department department;
}

Когда я использую следующий запрос, я получаю дубликаты объектов объекта:

session.createQuery("select dep from Department as dep left join dep.employees");

Таким образом, я должен использовать различные:

session.createQuery("select distinct dep from Department as dep left join dep.employees");

Является ли это ожидаемым? Я считаю это необычным, сравнивая его с SQL.

4b9b3361

Ответ 1

Этот вопрос подробно описан на Hibernate FAQ:

Во-первых, вам нужно понять SQL и как работают OUTER JOINs в SQL. Если вы не полностью понимаете и понимаете внешние соединения в SQL, не продолжите чтение этого пункта часто задаваемых вопросов, но обратитесь к руководству или учебнику по SQL. В противном случае вы не поймете следующее объяснение, и вы будет жаловаться на это поведение на форуме Hibernate. типичный примеры, которые могут возвращать повторяющиеся ссылки одного и того же ордера Объект:

List result = session.createCriteria(Order.class)  
                        .setFetchMode("lineItems", FetchMode.JOIN)  
                        .list();

<class name="Order">           
    <set name="lineItems" fetch="join">
    ...
</class>

List result = session.createCriteria(Order.class)  
                        .list();  

List result = session.createQuery("select o from Order o left join fetch o.lineItems").list();  

Все эти примеры производят один и тот же оператор SQL:

SELECT o.*, l.* from ORDER o LEFT OUTER JOIN LINE_ITEMS l ON o.ID = l.ORDER_ID   

Хотите знать, почему дубликаты есть? Посмотрите на SQL resultset, Hibernate не скрывает эти дубликаты с левой стороны внешнего результата, но возвращает все дубликаты вождение стол. Если у вас 5 заказов в базе данных и каждый заказ имеет 3 позиции, набор результатов будет 15 строк. Список результатов Java из этих запросов будет иметь 15 элементов, все типа Order. Только 5 Экземпляры заказов будут созданы Hibernate, но дубликаты Результаты SQL сохраняются как дубликаты ссылок на эти 5 экземпляров. Если вы не понимаете это последнее предложение, вам нужно читать на Java и разницу между экземпляром на Java кучу и ссылку на такой экземпляр. (Почему левое внешнее соединение? у вас будет дополнительный заказ без позиций, набор результатов будет 16 строк с заполнением NULL правой стороны, где строка Данные позиции предназначены для другого заказа. Вы хотите заказывать, даже если у них нет позиций, не так ли? Если нет, используйте внутреннюю выборку соединения в HQL).
Hibernate не отфильтровывает эти дубликаты ссылок по умолчанию. Некоторые люди (а не вы) на самом деле этого хотят. Как вы можете отфильтровать их? Вот так:

Collection result = new LinkedHashSet( session.create*(...).list() );  

A LinkedHashSet фильтрует дубликаты ссылок (это набор) и он сохраняет порядок вставки (порядок элементов в вашем результате). Что было слишком легко, поэтому вы можете сделать это во многих разных и более сложных способы:

List result = session.createCriteria(Order.class)  
                        .setFetchMode("lineItems", FetchMode.JOIN)  
                        .setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY)  
                        .list();  


<class name="Order">  
    ...  
    <set name="lineItems" fetch="join">  

List result = session.createCriteria(Order.class)  
                        .setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY)  
                        .list();  

List result = session.createQuery("select o from Order o left join fetch o.lineItems")  
                      .setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY) // Yes, really!  
                      .list();  

List result = session.createQuery("select distinct o from Order o left join fetch o.lineItems").list();       

Последний особенный. Похоже, вы используете SQL Здесь используется ключевое слово DISTINCT. Конечно, это не SQL, это HQL. Эта Различный - это просто ярлык для трансформатора результата, в данном случае. Да, в других случаях отличительная особенность HQL будет переводиться прямо в SQL DISTINCT. Не в этом случае: вы не можете отфильтровывать дубликаты на Уровень SQL, сама природа продукта /join запрещает это - вы хотите дубликаты или вы не получите все необходимые данные. Все это фильтрация дубликатов происходит в памяти, когда набор результатов объединенных в объекты. Также должно быть очевидно, почему результаты основанные на строке "лимитные" операции, такие как setFirstResult (5) и setMaxResults (10) не работают с этими типами запросов с надежной выборкой. Если вы ограничиваете набор результатов определенным числом строк, вы обрезаете данные случайным образом. Однажды Hibernate может быть достаточно умным, чтобы знать, что если вы вызываете setFirstResult() или setMaxResults(), он не должен использовать соединение, а второй SQL SELECT. Попробуйте, ваша версия Hibernate может уже достаточно умны. Если нет, напишите два запроса, один для ограничения другой, для желающих. Вы хотите знать, почему пример с запросом Criteria не игнорировал fetch = "join" в картировании, но HQL не волновало? Прочитайте следующий пункт часто задаваемых вопросов.