Вопрос по java, one-to-many, hibernate, hql, distinct – Отношение «один ко многим» позволяет получить дубликаты объектов без использования «различных». Зачем?

44

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

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

на самом деле запрос имеет предложение where, мне нужно поставить условие для каждого сотрудника иfrom Department не поможет мне в этом случае.select dep from Department as dep left join dep.employees emp where emp.enddate > current_date() herti
почему вы делаетеleft join, Простоfrom Department также ваш тип выборки стремится. Что такое вывод sql? nachokk

Ваш Ответ

1   ответ
81

Этот вопрос подробно объясняется наHibernate FAQ:

Во-первых, вам нужно понять SQL и как работают ВНЕШНИЕ СОЕДИНЕНИЯ в SQL. Если вы не до конца понимаете и понимаете внешние объединения в SQL, не продолжайте читать этот раздел часто задаваемых вопросов, а обратитесь к руководству или учебнику по SQL. В противном случае вы не поймете следующее объяснение, и вы будете жаловаться на такое поведение на форуме Hibernate. Типичные примеры, которые могут возвращать повторяющиеся ссылки одного и того же объекта Order:

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

           
    
    ...


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, Hibernate не скрывает эти дубликаты на левой стороне внешнего объединенного результата, но возвращает все дубликаты таблицы управления. Если у вас есть 5 заказов в базе данных, и у каждого заказа есть 3 позиции, результирующий набор будет 15 строк. Список результатов Java этих запросов будет иметь 15 элементов, все типа Order. Только 5 экземпляров Order будут созданы Hibernate, но дубликаты результирующего набора SQL будут сохранены как дубликаты ссылок на эти 5 экземпляров. Если вы не понимаете это последнее предложение, вам нужно прочитать о Java и о разнице между экземпляром в куче Java и ссылкой на такой экземпляр. (Почему левое внешнее соединение? Если выЕсли бы у меня был дополнительный порядок без позиций, результирующий набор был бы 16 строками с NULL, заполняющим правую сторону, где данные позиции были для другого порядка. Вы хотите заказы, даже если они неу вас нет позиций, верно? Если нет, используйте выборку внутреннего соединения в вашем HQL).

Hibernate не отфильтровывает эти дубликаты ссылок по умолчанию. Некоторые люди (а не вы) действительно хотят этого. Как вы можете отфильтровать их? Как это:

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

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

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


  
    ...  
      

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, сама природа продукта / объединения запрещает это - вы хотите дубликаты или вы неполучить все необходимые данные Вся эта фильтрация дубликатов происходит в памяти, когда результирующий набор объединяется в объекты. Должно быть также очевидно, почему набор результатов на основе строк "предел» Операции, такие как setFirstResult (5) и setMaxResults (10), не работают с подобными запросами на выборку. Если вы ограничите набор результатов определенным количеством строк, вы случайно обрежете данные. Однажды Hibernate может быть достаточно умен, чтобы знать, что если вы вызываете setFirstResult () или setMaxResults (), он должен использовать не соединение, а второй SQL SELECT. Попробуйте, ваша версия Hibernate уже может быть достаточно умной. Если нет, напишите два запроса, один для ограничения содержимого, другой для получения извлечения. Вы хотите знать, почему пример с запросом Criteria не игнорировал fetch = "присоединиться" установка в отображении, но HQL не сделалне волнует? Прочитайте следующий пункт FAQ.

У меня проблемы с производительностью с этим. Я думаю, что, может быть, Hibernate пытается создать объекты Java для каждого из дублированных элементов, а затем делает отдельные? Так что в основном создание большого количества ненужных объектов вызывает замедление, так ли это? Есть ли более эффективное решение? mekane84

Похожие вопросы