Ответ 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
поможет, но это не ваш случай.