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

Я использую 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 не помогают, так как там нет дубликатов. Я мог бы вручную удалить те записи, которые были бы весьма неэффективными, я думаю.

Ответ 1

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

@OneToMany
private Set<AttributeY> attributeY;

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

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

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

Единственное отличие состоит в том, что 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 а в других - нет. Но вы можете попытаться реализовать методы equals/hashCode используя "бизнес-ключ" сущности:

При использовании наборов очень важно предоставить правильные реализации 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
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 поможет, но это не ваш случай.

Ответ 2

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