Masalah yang Anda uji coba berkaitan dengan cara Anda menggunakan HINT_PASS_DISTINCT_THROUGH
petunjuk.
Petunjuk ini memungkinkan Anda untuk menunjukkan Hibernate bahwa DISTINCT
kata kunci tidak boleh digunakan dalam SELECT
pernyataan yang dikeluarkan terhadap database.
Anda memanfaatkan fakta ini untuk memungkinkan kueri Anda diurutkan menurut bidang yang tidak disertakan dalam DISTINCT
daftar kolom.
Namun, petunjuk ini seharusnya tidak digunakan.
Petunjuk ini hanya harus digunakan jika Anda yakin tidak akan ada perbedaan antara menerapkan atau tidak menggunakan DISTINCT
kata kunci ke SQL SELECT
pernyataan, karena SELECT
pernyataan sudah akan mengambil semua nilai yang berbeda per se . Idenya adalah meningkatkan kinerja kueri dengan menghindari penggunaan DISTINCT
yang tidak perlu pernyataan.
Ini biasanya yang akan terjadi jika Anda menggunakan query.distinct
metode dalam kueri kriteria Anda, dan Anda join fetching
hubungan anak. Artikel bagus ini
dari @VladMihalcea menjelaskan cara kerja petunjuk secara detail.
Di sisi lain, ketika Anda menggunakan paging, itu akan mengatur OFFSET
dan LIMIT
- atau yang serupa, tergantung pada basis data yang mendasarinya - dalam SQL SELECT
pernyataan yang dikeluarkan terhadap database, membatasi jumlah maksimum hasil kueri Anda.
Seperti yang dinyatakan, jika Anda menggunakan HINT_PASS_DISTINCT_THROUGH
petunjuk, SELECT
pernyataan tidak akan berisi DISTINCT
kata kunci dan, karena gabungan Anda, itu berpotensi memberikan catatan duplikat dari entitas utama Anda. Catatan ini akan diproses oleh Hibernate untuk membedakan duplikat, karena Anda menggunakan query.distinct
, dan itu sebenarnya akan menghapus duplikat jika diperlukan. Saya rasa inilah alasan mengapa Anda mungkin mendapatkan lebih sedikit catatan daripada yang diminta di Pageable
.
Jika Anda menghapus petunjuk, sebagai DISTINCT
kata kunci dilewatkan dalam pernyataan SQL yang dikirim ke database, sejauh Anda hanya memproyeksikan informasi entitas utama, itu akan mengambil semua catatan yang ditunjukkan oleh LIMIT
dan inilah mengapa ia akan selalu memberi Anda jumlah catatan yang diminta.
Anda dapat mencoba dan fetch join
entitas anak Anda (bukan hanya join
dengan mereka). Ini akan menghilangkan masalah tidak dapat menggunakan bidang yang perlu Anda urutkan di kolom DISTINCT
kata kunci dan, sebagai tambahan, Anda akan dapat menerapkan, sekarang secara sah, petunjuknya.
Tetapi jika Anda melakukannya, itu akan menimbulkan masalah lain bagi Anda:jika Anda menggunakan join fetch dan pagination, untuk mengembalikan entitas utama dan koleksinya, Hibernate tidak akan lagi menerapkan pagination pada tingkat database - ini tidak akan menyertakan OFFSET
atau LIMIT
kata kunci dalam pernyataan SQL, dan akan mencoba membuat paginasi hasilnya di memori. Ini adalah HHH000104
Hibernate yang terkenal peringatan:
HHH000104: firstResult/maxResults specified with collection fetch; applying in memory!
@VladMihalcea menjelaskannya dengan sangat rinci di bagian terakhir ini artikel.
Dia juga mengusulkan satu solusi yang mungkin untuk masalah Anda, Fungsi Jendela .
Dalam kasus penggunaan Anda, alih-alih menggunakan Specification
s, idenya adalah Anda mengimplementasikan DAO Anda sendiri. DAO ini hanya perlu memiliki akses ke EntityManager
, yang tidak banyak karena Anda dapat menyuntikkan @PersistenceContext
. Anda :
@PersistenceContext
protected EntityManager em;
Setelah Anda memiliki EntityManager
ini , Anda dapat membuat kueri asli dan menggunakan fungsi jendela untuk membangun, berdasarkan Pageable
. yang disediakan informasi, pernyataan SQL yang tepat yang akan dikeluarkan terhadap database. Ini akan memberi Anda lebih banyak kebebasan tentang bidang apa yang digunakan untuk menyortir atau apa pun yang Anda butuhkan.
Seperti yang ditunjukkan oleh artikel yang dikutip terakhir, Fungsi Jendela adalah fitur yang didukung oleh semua basis data walikota.
Dalam kasus PostgreSQL, Anda dapat dengan mudah menemukannya di dokumentasi resmi .
Akhirnya, satu opsi lagi, sebenarnya disarankan oleh @nickshoe, dan dijelaskan dengan sangat rinci di artikel dia mengutip, adalah melakukan proses penyortiran dan paging dalam dua fase:pada fase pertama, Anda perlu membuat kueri yang akan mereferensikan entitas anak Anda dan di mana Anda akan menerapkan paging dan pengurutan. Kueri ini akan memungkinkan Anda untuk mengidentifikasi id entitas utama yang akan digunakan, pada tahap kedua proses, untuk mendapatkan entitas utama itu sendiri.
Anda dapat memanfaatkan DAO khusus yang disebutkan di atas untuk menyelesaikan proses ini.