Det problem, du eksperimenterer, har at gøre med den måde, du bruger HINT_PASS_DISTINCT_THROUGH
på tip.
Dette tip giver dig mulighed for at angive Hibernate, at DISTINCT
nøgleord bør ikke bruges i SELECT
erklæring udstedt mod databasen.
Du udnytter dette faktum til at tillade, at dine forespørgsler sorteres efter et felt, der ikke er inkluderet i DISTINCT
kolonneliste.
Men det er ikke sådan dette tip skal bruges.
Dette tip skal kun bruges, når du er sikker på, at der ikke vil være nogen forskel mellem at anvende eller ikke anvende en DISTINCT
nøgleord til SQL SELECT
sætning, fordi SELECT
sætningen vil allerede hente alle de forskellige værdier i sig selv . Ideen er at forbedre ydelsen af forespørgslen og undgå brugen af en unødvendig DISTINCT
erklæring.
Dette er normalt, hvad der vil ske, når du bruger query.distinct
metode i dine kriterieforespørgsler, og du join fetching
børneforhold. Denne fantastiske artikel
af @VladMihalcea forklare, hvordan hintet fungerer i detaljer.
På den anden side, når du bruger personsøgning, vil den indstille OFFSET
og LIMIT
- eller noget lignende, afhængigt af den underliggende database - i SQL SELECT
erklæring udstedt mod databasen, hvilket begrænser din forespørgsel til et maksimalt antal resultater.
Som nævnt, hvis du bruger HINT_PASS_DISTINCT_THROUGH
tip, SELECT
sætningen vil ikke indeholde DISTINCT
søgeord, og på grund af dine joinforbindelser kan det potentielt give dublerede registreringer af din hovedentitet. Disse poster vil blive behandlet af Hibernate for at differentiere dubletter, fordi du bruger query.distinct
, og det vil faktisk fjerne dubletter, hvis det er nødvendigt. Jeg tror, at dette er grunden til, at du muligvis får færre poster end anmodet om i din Pageable
.
Hvis du fjerner tippet, som DISTINCT
nøgleordet sendes i SQL-sætningen, som sendes til databasen, så vidt du kun projekterer oplysninger om hovedenheden, vil det hente alle de poster, der er angivet med LIMIT
og det er derfor, det altid vil give dig det ønskede antal poster.
Du kan prøve at fetch join
dine underordnede enheder (i stedet for kun join
med dem). Det vil eliminere problemet med ikke at kunne bruge det felt, du skal sortere efter i kolonnerne i DISTINCT
søgeord, og derudover vil du være i stand til at anvende, nu lovligt, tippet.
Men hvis du gør det, vil det give dig et andet problem:Hvis du bruger join-hentning og paginering for at returnere hovedentiteterne og dets samlinger, vil Hibernate ikke længere anvende paginering på databaseniveau - det vil ikke inkludere OFFSET
eller LIMIT
nøgleord i SQL-sætningen, og den vil forsøge at paginere resultaterne i hukommelsen. Dette er den berømte Hibernate HHH000104
advarsel:
HHH000104: firstResult/maxResults specified with collection fetch; applying in memory!
@VladMihalcea forklarer det meget detaljeret i den sidste del af dette artikel.
Han foreslog også en mulig løsning på dit problem, Window Functions .
I dit tilfælde, i stedet for at bruge Specification
s, tanken er, at du implementerer din egen DAO. Denne DAO behøver kun at have adgang til EntityManager
, hvilket ikke er meget, da du kan injicere din @PersistenceContext
:
@PersistenceContext
protected EntityManager em;
Når du har denne EntityManager
, kan du oprette indbyggede forespørgsler og bruge vinduesfunktioner til at opbygge, baseret på den medfølgende Pageable
information, den rigtige SQL-sætning, der vil blive udstedt mod databasen. Dette vil give dig meget mere frihed med hensyn til, hvilke felter der bruges til sortering eller hvad du har brug for.
Som den sidst citerede artikel angiver, er vinduesfunktioner en funktion, der understøttes af alle borgmesterdatabaser.
I tilfælde af PostgreSQL kan du nemt støde på dem i den officielle dokumentation .
Til sidst en mulighed mere, faktisk foreslået af @nickshoe, og forklaret meget detaljeret i artikel han citerede, er at udføre sorterings- og personsøgningsprocessen i to faser:I den første fase skal du oprette en forespørgsel, der refererer til dine underordnede enheder, og hvor du vil anvende personsøgning og sortering. Denne forespørgsel vil give dig mulighed for at identificere id'erne for de vigtigste enheder, der vil blive brugt i anden fase af processen til at få selve hovedenhederne.
Du kan drage fordel af den førnævnte brugerdefinerede DAO til at udføre denne proces.