Het probleem dat u aan het experimenteren bent, heeft te maken met de manier waarop u de HINT_PASS_DISTINCT_THROUGH
gebruikt hint.
Met deze hint kun je Hibernate aangeven dat de DISTINCT
trefwoord mag niet worden gebruikt in de SELECT
verklaring afgegeven tegen de database.
U maakt gebruik van dit feit om uw zoekopdrachten te laten sorteren op een veld dat niet is opgenomen in de DISTINCT
kolomlijst.
Maar zo moet deze hint niet worden gebruikt.
Deze hint moet alleen worden gebruikt als u zeker weet dat er geen verschil zal zijn tussen het wel of niet toepassen van een DISTINCT
sleutelwoord naar de SQL SELECT
statement, omdat de SELECT
statement haalt al de verschillende waarden per se . Het idee is om de prestaties van de zoekopdracht te verbeteren en het gebruik van een onnodige DISTINCT
. te vermijden verklaring.
Dit is meestal wat er zal gebeuren als u de query.distinct
. gebruikt methode in je criteriaquery's, en je bent join fetching
kind relaties. Dit geweldige artikel
van @VladMihalcea leggen in detail uit hoe de hint werkt.
Aan de andere kant, wanneer u paging gebruikt, wordt OFFSET
. ingesteld en LIMIT
- of iets dergelijks, afhankelijk van de onderliggende database - in de SQL SELECT
verklaring afgegeven tegen de database, die uw zoekopdracht tot een maximum aantal resultaten beperkt.
Zoals vermeld, als u de HINT_PASS_DISTINCT_THROUGH
hint, de SELECT
statement bevat niet de DISTINCT
trefwoord en, vanwege uw joins, kan het mogelijk dubbele records van uw hoofdentiteit opleveren. Deze records worden door Hibernate verwerkt om dubbele gegevens te onderscheiden, omdat u query.distinct
gebruikt , en het zal in feite duplicaten verwijderen indien nodig. Ik denk dat dit de reden is waarom u mogelijk minder records krijgt dan gevraagd in uw Pageable
.
Als u de hint verwijdert, zoals de DISTINCT
sleutelwoord wordt doorgegeven in de SQL-instructie die naar de database wordt verzonden, voor zover u alleen informatie van de hoofdentiteit projecteert, zal het alle records ophalen die worden aangegeven door LIMIT
en daarom krijgt u altijd het gevraagde aantal records.
Je kunt proberen en fetch join
uw onderliggende entiteiten (in plaats van alleen join
met hen). Het lost het probleem op dat u het veld waarop u moet sorteren niet kunt gebruiken in de kolommen van de DISTINCT
zoekwoord en bovendien kunt u de hint nu legitiem toepassen.
Maar als u dit doet, krijgt u een ander probleem:als u samen ophaalt en paginering gebruikt om de belangrijkste entiteiten en de bijbehorende verzamelingen te retourneren, past Hibernate geen paginering meer toe op databaseniveau - het bevat geen OFFSET
of LIMIT
trefwoorden in de SQL-instructie, en het zal proberen de resultaten in het geheugen te pagineren. Dit is de beroemde Hibernate HHH000104
waarschuwing:
HHH000104: firstResult/maxResults specified with collection fetch; applying in memory!
@VladMihalcea legt dat in detail uit in het laatste deel van dit artikel.
Hij stelde ook een mogelijke oplossing voor uw probleem voor, Window Functions .
In uw geval, in plaats van Specification
. te gebruiken s, het idee is dat je je eigen DAO implementeert. Deze DAO hoeft alleen toegang te hebben tot de EntityManager
, wat niet veel is, aangezien u uw @PersistenceContext
. kunt injecteren :
@PersistenceContext
protected EntityManager em;
Zodra u deze EntityManager
. heeft , kunt u native query's maken en vensterfuncties gebruiken om te bouwen, op basis van de meegeleverde Pageable
informatie, de juiste SQL-instructie die tegen de database wordt uitgegeven. Dit geeft je veel meer vrijheid over welke velden worden gebruikt om te sorteren of wat je ook nodig hebt.
Zoals het laatst geciteerde artikel aangeeft, is Window Functions een functie die door alle grote databases wordt ondersteund.
In het geval van PostgreSQL kun je ze gemakkelijk tegenkomen in de officiële documentatie .
Eindelijk nog een optie, in feite voorgesteld door @nickshoe, en in detail uitgelegd in de artikel hij citeerde, is om het sorteer- en pagingproces in twee fasen uit te voeren:in de eerste fase moet je een query maken die verwijst naar je onderliggende entiteiten en waarin je paging en sortering toepast. Met deze zoekopdracht kunt u de ID's van de belangrijkste entiteiten identificeren die in de tweede fase van het proces worden gebruikt om de belangrijkste entiteiten zelf te verkrijgen.
U kunt profiteren van de eerder genoemde aangepaste DAO om dit proces te volbrengen.