Дубликаты с использованием выборки с левым соединением

Я использую HQL-запрос для получения определенных записей. Если я использую LEFT JOIN FETCH, коллекция, которая находится в моем целевом объекте, будет содержать повторяющиеся записи. Если я использую только левое соединение, этого не произойдет. Я думаю, когда Hibernate лениво загружает записи, он избегает дублирования.

"SELECT z FROM ", TableA.TABLE_NAME, " z ",  //
            "LEFT JOIN FETCH z.attributeX pv ", //
            "LEFT JOIN FETCH pv.attributeY anteil ", //
            "LEFT JOIN FETCH z.attributeB kma ", //
            "LEFT JOIN FETCH kma.attributeC ", //
            "WHERE anteil.attributeD.attributeE.id = :eId ", //
            "AND pv.attributeG.id = :gId ");

Моя сущность TableA имеет ссылку на TablePV (LEFT JOIN FETCH z.attributeX pv)

TablePV имеет коллекцию TableY (LEFT JOIN FETCH pv.attributeY anteil)

Теперь Hibernate будет правильно отображать все, кроме дочерних элементов TablePV. Он будет содержать одну и ту же запись несколько раз. Различный на TableA не помогает, так как там нет дубликатов. Я мог бы вручную удалить эти записи, которые, я полагаю, были бы совершенно неэффективными.


person Daniel    schedule 15.03.2012    source источник
comment
Используйте набор как свою коллекцию.   -  person JB Nizet    schedule 15.03.2012
comment
AFAIK LEFT JOIN FETCH и ограничение на него являются взаимоисключающими, поскольку ограничение может отфильтровывать элементы коллекции для извлечения нетерпеливых. Может быть, у вас другая первопричина   -  person Firo    schedule 16.03.2012
comment
Спасибо за ответы. Набор решит проблему, но является временным решением и создаст другие проблемы (например, заказ). Я не знаю, является ли это взаимоисключающим, поскольку случай имеет смысл. Я хочу, чтобы записи выбирались в одном запросе, а не в другом операторе выбора. Это произошло бы, если бы я использовал только LEFT JOIN без FETCH.   -  person Daniel    schedule 16.03.2012
comment
Как Set удалить повторяющиеся объекты в свойствах коллекции ?!   -  person The Light    schedule 24.07.2013
comment
Узнайте, что возвращает LEFT JOIN: строки INNER JOIN плюс несопоставленные строки левой таблицы, расширенные на NULL. Всегда знайте, какое INNER JOIN вы хотите как часть LEFT JOIN. WHERE или INNER ON, требующие, чтобы правый столбец таблицы не был NULL, после того, как LEFT JOIN ON удаляет все строки, расширенные NULL, т.е. оставляет только строки INNER JOIN, т.е. превращает LEFT JOIN во INNER JOIN. Это у тебя есть.   -  person philipxy    schedule 20.12.2018


Ответы (2)


Единственный способ действительно гарантировать - использовать Set или SortedSet в этих коллекциях вместо использования List. Официально нет другого способа избежать этой проблемы с помощью Hibernate:

@OneToMany
private Set<AttributeY> attributeY;

Вы можете прочитать этот совет в старом Hibernate документация:

Запросы, которые используют активную выборку коллекций, обычно возвращают дубликаты корневых объектов, но с их инициализированными коллекциями. Вы можете отфильтровать эти дубликаты с помощью Set.

Или какая-то ссылка на ту же проблему на более новом :

Единственное отличие состоит в том, что Set не допускает дублирования, но это ограничение обеспечивается контрактом объекта Java, а не отображением базы данных.

Установить и заказать

Если вы хотите использовать Set и контролировать порядок объектов, можно использовать SortedSet и реализует Comparable для дочерних сущностей:

@OneToMany
@SortNatural
private SortedSet<AttributeY> attributeY = new TreeSet<>();

И:

@Entity
public class AttributeY implements Comparable<AttributeY> {

    @Override
    public int compareTo(AttributeY o) {
        return number.compareTo( o.getNumber() );
    }

}

Для настраиваемой логики сортировки вы можете использовать @SortComparator.

Меры предосторожности

Без дополнительных подробностей трудно сказать, почему в одних случаях это происходит при использовании List, а в других - нет. Но вы можете попробовать реализовать _12 _ / _ 13_ методы, используя «бизнес-ключ» сущности:

При использовании наборов очень важно предоставить правильные реализации equals / hashCode для дочерних сущностей. В отсутствие настраиваемой логики реализации equals / hashCode Hibernate будет использовать стандартное равенство объектов Java на основе ссылок, что может привести к неожиданным результатам при смешивании экземпляров отдельных и управляемых объектов.

Кроме того, вы применяете условие, используя FETCH псевдонимы pv и anteil. Не делайте это. И избавьтесь от «крушения поезда» на вашем JPQL (anteil.attributeD.attributeE.id), потому что это может заставить Hibernate создавать странные SQL-запросы (например, выполнение одного и того же JOIN более одного раза или недопустимые SQL-запросы). Итак, сделайте JOIN явными и не используйте псевдоним FETCH на WHERE:

LEFT JOIN FETCH z.attributeX
LEFT JOIN FETCH pv.attributeY
LEFT JOIN FETCH z.attributeB kma
LEFT JOIN FETCH kma.attributeC
LEFT JOIN pv.attributeY anteil
LEFT JOIN anteil.attributeD attributeD
LEFT JOIN attributeD.attributeE attributeE
LEFT JOIN z.attributeX pv
LEFT JOIN pv.attributeG attributeG
WHERE attributeE.id = :eId 
AND attributeG.id = :gId

Если дублирование было в корневом объекте TableA, DISTINCT поможет, но это не ваш случай.

person Dherik    schedule 19.12.2018

Попробуйте использовать DISTINCT в своем запросе, например, SELECT DISTINCT z FROM Entity z...

person Juan Francisco Navarrete Martn    schedule 22.09.2012
comment
Как упоминалось в моем вопросе, это не помогает. Проблема в том, что коллекция содержит дубликаты, а не сам основной объект. - person Daniel; 02.10.2012