Дубликати с помощта на ляво свързване на извличане

Използвам 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 не позволява дубликати, но това ограничение се налага от договора за 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 за дъщерни обекти. При липса на персонализирана логика за изпълнение на равенства/хешкод, 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