Problem, z którym eksperymentujesz, jest związany ze sposobem korzystania z HINT_PASS_DISTINCT_THROUGH
wskazówka.
Ta wskazówka pozwala wskazać Hibernate, że DISTINCT
słowo kluczowe nie powinno być używane w SELECT
oświadczenie wydane w bazie danych.
Korzystasz z tego faktu, aby umożliwić sortowanie zapytań według pola, które nie jest zawarte w DISTINCT
lista kolumn.
Ale nie tak należy używać tej wskazówki.
Ta wskazówka musi być używana tylko wtedy, gdy masz pewność, że nie będzie różnicy między zastosowaniem lub nie DISTINCT
słowo kluczowe do SQL SELECT
oświadczenie, ponieważ SELECT
instrukcja już pobierze wszystkie odrębne wartości per se . Pomysł polega na poprawie wydajności zapytania, unikając niepotrzebnego DISTINCT
oświadczenie.
Zwykle dzieje się tak, gdy użyjesz query.distinct
metoda w zapytaniach kryterialnych i join fetching
relacje z dziećmi. Ten wspaniały artykuł
@VladMihalcea wyjaśnij szczegółowo, jak działa podpowiedź.
Z drugiej strony, gdy używasz stronicowania, ustawi OFFSET
i LIMIT
- lub coś podobnego, w zależności od bazowej bazy danych - w SQL SELECT
oświadczenie wydane w stosunku do bazy danych, ograniczające maksymalną liczbę wyników zapytania.
Jak wspomniano, jeśli użyjesz HINT_PASS_DISTINCT_THROUGH
wskazówka, SELECT
instrukcja nie będzie zawierać DISTINCT
słowa kluczowego, a ze względu na twoje sprzężenia może potencjalnie dać zduplikowane rekordy twojej głównej encji. Te rekordy będą przetwarzane przez Hibernate w celu rozróżnienia duplikatów, ponieważ używasz query.distinct
, aw razie potrzeby usunie duplikaty. Myślę, że to jest powód, dla którego możesz otrzymać mniej rekordów niż żądano w swoim Pageable
.
Jeśli usuniesz wskazówkę, jako DISTINCT
słowo kluczowe jest przekazywane w instrukcji SQL, która jest wysyłana do bazy danych, o ile projektujesz tylko informacje o głównej encji, pobierze wszystkie rekordy wskazane przez LIMIT
i dlatego zawsze da ci żądaną liczbę rekordów.
Możesz spróbować fetch join
twoje jednostki podrzędne (zamiast tylko join
z nimi). Wyeliminuje to problem braku możliwości użycia pola, według którego chcesz sortować w kolumnach DISTINCT
słowa kluczowego, a ponadto będziesz mógł zastosować, teraz zgodnie z prawem, podpowiedź.
Ale jeśli to zrobisz, pojawi się kolejny problem:jeśli użyjesz łączenia pobierania i paginacji, aby zwrócić główne encje i ich kolekcje, Hibernate nie będzie już stosować paginacji na poziomie bazy danych - nie będzie zawierać OFFSET
lub LIMIT
słowa kluczowe w instrukcji SQL i spróbuje pogrupować wyniki w pamięci. To jest słynny Hibernate HHH000104
ostrzeżenie:
HHH000104: firstResult/maxResults specified with collection fetch; applying in memory!
@VladMihalcea wyjaśnij to bardzo szczegółowo w ostatniej części tego artykuł.
Zaproponował również jedno możliwe rozwiązanie twojego problemu, Funkcje okien .
Używasz przypadku, zamiast używać Specification
Chodzi o to, aby zaimplementować własne DAO. To DAO musi mieć tylko dostęp do EntityManager
, co nie jest zbyt wiele, ponieważ możesz wstrzyknąć swój @PersistenceContext
:
@PersistenceContext
protected EntityManager em;
Po uzyskaniu tego EntityManager
, możesz tworzyć natywne zapytania i używać funkcji okna do budowania na podstawie dostarczonego Pageable
informacje, właściwa instrukcja SQL, która zostanie wydana w bazie danych. Daje to o wiele więcej swobody w wyborze pól używanych do sortowania lub czegokolwiek, czego potrzebujesz.
Jak wskazuje ostatni cytowany artykuł, Funkcje okien to funkcja obsługiwana przez wszystkie główne bazy danych.
W przypadku PostgreSQL można je łatwo znaleźć w oficjalnej dokumentacji .
Na koniec jeszcze jedna opcja, zasugerowana przez @nickshoe i wyjaśniona szczegółowo w artykuł cytował, polega na przeprowadzeniu procesu sortowania i stronicowania w dwóch fazach:w pierwszej fazie należy utworzyć zapytanie, które będzie odwoływać się do twoich encji podrzędnych i w którym zastosujesz stronicowanie i sortowanie. To zapytanie pozwoli Ci zidentyfikować identyfikatory głównych encji, które zostaną użyte w drugiej fazie procesu, aby uzyskać same główne encje.
Możesz skorzystać z wyżej wspomnianego niestandardowego DAO, aby zrealizować ten proces.