PostgreSQL
 sql >> Database >  >> RDS >> PostgreSQL

Suggerimento HINT_PASS_DISTINCT_THROUGH riduce la quantità di entità restituite per pagina per una richiesta di pagina al di sotto della dimensione della pagina configurata (PostgreSQL)

Il problema che stai sperimentando ha a che fare con il modo in cui stai usando HINT_PASS_DISTINCT_THROUGH suggerimento.

Questo suggerimento ti permette di indicare a Hibernate che il DISTINCT la parola chiave non deve essere utilizzata in SELECT dichiarazione rilasciata contro il database.

Stai sfruttando questo fatto per consentire alle tue query di essere ordinate in base a un campo che non è incluso in DISTINCT elenco di colonne.

Ma non è così che dovrebbe essere usato questo suggerimento.

Questo suggerimento deve essere utilizzato solo quando sei sicuro che non ci sarà alcuna differenza tra l'applicazione o meno di un DISTINCT parola chiave all'SQL SELECT istruzione, perché SELECT istruzione recupererà già tutti i valori distinti di per sé . L'idea è di migliorare le prestazioni della query evitando l'uso di un DISTINCT non necessario dichiarazione.

Di solito è ciò che accadrà quando usi query.distinct metodo nelle tue query sui criteri e sei join fetching relazioni infantili. Questo fantastico articolo di @VladMihalcea spiega in dettaglio come funziona il suggerimento.

D'altra parte, quando usi il paging, imposterà OFFSET e LIMIT - o qualcosa di simile, a seconda del database sottostante - in SQL SELECT dichiarazione rilasciata contro il database, limitando a un numero massimo di risultati la tua query.

Come detto, se usi il HINT_PASS_DISTINCT_THROUGH suggerimento, il SELECT l'istruzione non conterrà il DISTINCT parola chiave e, a causa dei tuoi join, potrebbe potenzialmente fornire record duplicati della tua entità principale. Questi record verranno elaborati da Hibernate per differenziare i duplicati, poiché stai utilizzando query.distinct , e in effetti rimuoverà i duplicati se necessario. Penso che questo sia il motivo per cui potresti ottenere meno record di quanto richiesto nel tuo Pageable .

Se rimuovi il suggerimento, come DISTINCT viene passata nell'istruzione SQL che viene inviata al database, per quanto si proiettino solo le informazioni dell'entità principale, recupererà tutti i record indicati da LIMIT ed è per questo che ti darà sempre il numero di record richiesto.

Puoi provare a fetch join le tue entità figlio (anziché solo join con loro). Eliminerà il problema di non poter utilizzare il campo che devi ordinare nelle colonne del DISTINCT parola chiave e, inoltre, potrai applicare, ora legittimamente, il suggerimento.

Ma se lo fai, avrai un altro problema:se usi join fetch e impaginazione, per restituire le entità principali e le sue raccolte, Hibernate non applicherà più l'impaginazione a livello di database - non includerà OFFSET o LIMIT parole chiave nell'istruzione SQL e proverà a impaginare i risultati in memoria. Questo è il famoso HHH000104 di Hibernate avviso:

HHH000104: firstResult/maxResults specified with collection fetch; applying in memory!

@VladMihalcea lo spiega in dettaglio nell'ultima parte di questo articolo.

Ha anche proposto una possibile soluzione al tuo problema, Funzioni finestra .

Nel tuo caso d'uso, invece di usare Specification s, l'idea è di implementare il proprio DAO. Questo DAO deve solo avere accesso a EntityManager , il che non è un granché dato che puoi inserire il tuo @PersistenceContext :

@PersistenceContext
protected EntityManager em;

Una volta ottenuto questo EntityManager , puoi creare query native e utilizzare le funzioni della finestra per creare, in base al Pageable fornito informazioni, l'istruzione SQL corretta che verrà emessa rispetto al database. Questo ti darà molta più libertà su quali campi utilizzare per l'ordinamento o qualunque cosa ti serva.

Come indica l'ultimo articolo citato, Window Functions è una funzionalità supportata da tutti i principali database.

Nel caso di PostgreSQL, puoi trovarli facilmente nella documentazione ufficiale .

Infine, un'altra opzione, suggerita appunto da @nickshoe, e spiegata in dettaglio nel articolo ha citato, consiste nell'eseguire il processo di ordinamento e impaginazione in due fasi:nella prima fase, è necessario creare una query che farà riferimento alle entità figlio e in cui applicherai il paging e l'ordinamento. Questa query ti consentirà di identificare gli ID delle entità principali che verranno utilizzate, nella seconda fase del processo, per ottenere le entità principali stesse.

Puoi sfruttare il summenzionato DAO personalizzato per eseguire questo processo.