Problemet du experimenterar har att göra med hur du använder HINT_PASS_DISTINCT_THROUGH
ledtråd.
Detta tips låter dig indikera Hibernate att DISTINCT
nyckelord ska inte användas i SELECT
uttalande utfärdat mot databasen.
Du utnyttjar detta faktum för att tillåta att dina frågor sorteras efter ett fält som inte ingår i DISTINCT
kolumnlista.
Men det är inte så det här tipset ska användas.
Denna ledtråd måste endast användas när du är säker på att det inte kommer att vara någon skillnad mellan att tillämpa eller inte använda en DISTINCT
nyckelord till SQL SELECT
sats, eftersom SELECT
uttalandet kommer redan att hämta alla distinkta värden i och för sig . Tanken är att förbättra prestandan för frågan och undvika användningen av en onödig DISTINCT
uttalande.
Detta är vanligtvis vad som händer när du använder query.distinct
metod i dina kriteriefrågor, och du join fetching
barnrelationer. Denna fantastiska artikel
av @VladMihalcea förklara hur tipset fungerar i detalj.
Å andra sidan, när du använder personsökning kommer den att ställa in OFFSET
och LIMIT
- eller något liknande, beroende på den underliggande databasen - i SQL SELECT
uttalande som utfärdats mot databasen, vilket begränsar din fråga till ett maximalt antal resultat.
Som sagt, om du använder HINT_PASS_DISTINCT_THROUGH
ledtråd, SELECT
satsen kommer inte att innehålla DISTINCT
sökord och, på grund av dina kopplingar, kan det potentiellt ge dubbletter av din huvudenhet. Dessa poster kommer att bearbetas av Hibernate för att skilja dubbletter, eftersom du använder query.distinct
, och det kommer faktiskt att ta bort dubbletter om det behövs. Jag tror att detta är anledningen till att du kan få färre poster än vad som begärts i din Pageable
.
Om du tar bort tipset, som DISTINCT
nyckelordet skickas i SQL-satsen som skickas till databasen, så långt du bara projicerar information om huvudenheten, kommer det att hämta alla poster som anges med LIMIT
och det är därför du alltid får det begärda antalet poster.
Du kan försöka fetch join
dina underordnade enheter (istället för att bara join
). med dem). Det kommer att eliminera problemet med att inte kunna använda fältet du behöver sortera efter i kolumnerna i DISTINCT
nyckelord och dessutom kommer du att kunna tillämpa, nu på ett legitimt sätt, tipset.
Men om du gör det kommer du att få ett annat problem:om du använder join-hämtning och paginering, för att returnera huvudenheterna och dess samlingar, kommer Hibernate inte längre att tillämpa paginering på databasnivå - det kommer inte att inkludera OFFSET
eller LIMIT
nyckelord i SQL-satsen, och den kommer att försöka paginera resultaten i minnet. Detta är den berömda Hibernate HHH000104
varning:
HHH000104: firstResult/maxResults specified with collection fetch; applying in memory!
@VladMihalcea förklara det mycket detaljerat i den sista delen av detta artikel.
Han föreslog också en möjlig lösning på ditt problem, Fönsterfunktioner .
I ditt använda fall istället för att använda Specification
s, tanken är att du implementerar din egen DAO. Denna DAO behöver bara ha tillgång till EntityManager
, vilket inte är så mycket eftersom du kan injicera din @PersistenceContext
:
@PersistenceContext
protected EntityManager em;
När du har den här EntityManager
, kan du skapa inbyggda frågor och använda fönsterfunktioner för att bygga, baserat på den tillhandahållna Pageable
information, rätt SQL-sats som kommer att utfärdas mot databasen. Detta kommer att ge dig mycket mer frihet om vilka fält som används för sortering eller vad du behöver.
Som den senast citerade artikeln indikerar är Window Functions en funktion som stöds av alla borgmästardatabaser.
När det gäller PostgreSQL kan du enkelt hitta dem i den officiella dokumentationen .
Slutligen, ytterligare ett alternativ, faktiskt föreslagit av @nickshoe, och förklarat i detalj i artikel han citerade, är att utföra sorterings- och personsökningsprocessen i två faser:i den första fasen måste du skapa en fråga som refererar till dina underordnade enheter och där du kommer att tillämpa personsökning och sortering. Den här frågan låter dig identifiera ID:n för huvudenheterna som kommer att användas, i den andra fasen av processen, för att erhålla själva huvudenheterna.
Du kan dra nytta av den tidigare nämnda anpassade DAO för att utföra denna process.