Дубликаты с использованием левого соединения - PullRequest
11 голосов
/ 15 марта 2012

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

Ответы [ 2 ]

0 голосов
/ 20 декабря 2018

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

0 голосов
/ 22 сентября 2012

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

...