Spring Data JPA - Zapytania Natywne

W sytuacjach, gdzie ani zapytania wbudowane, ani własne nie spełniają oczekiwań, zapytania natywne mogą stanowić skuteczną alternatywę. Te zapytania, choć prostsze w idei, oferują znacznie szersze możliwości.

W skrócie, zapytania natywne pozwalają na bezpośrednie włączenie standardowego zapytania SQL do adnotacji @Query, zamiast wykorzystania JPQL. Dzięki temu, operacje są przeprowadzane bezpośrednio na tabelach, a nie na encjach, co daje większą swobodę działania. Korzystając z zapytań natywnych, mamy dostęp do pełnego spektrum możliwości języka SQL, bez ograniczeń narzuconych przez JPQL, co pozwala na bardziej zaawansowane i skomplikowane operacje na danych.

Zapytanie grupujące jako Native Query

Załóżmy, że naszym zadaniem jest opracowanie zapytania umożliwiającego prezentację statystyk dotyczących liczby itemów, podzielonych według określonych kategorii. Aby sprostać temu wyzwaniu, potrzebne będzie skonstruowanie zapytania grupującego, wykorzystującego klauzulę GROUP BY.

To zapytanie będzie realizowane w ramach kontrolowanego przez JPARepository. Oznacza to, że będziemy tworzyć złożoną konstrukcję, która pozwoli na efektywne gromadzenie i prezentowanie danych w sposób uporządkowany i przystępny dla użytkowników.
public interface ItemRepository extends JpaRepository<Item, Long> {

    @Query(value = "select ac.name, count(aci.items_id) from appa_categories_items aci "
            + "inner join appa_categories ac on ac.id=aci.categories_id "
            + "group by aci.categories_id", nativeQuery = true)
    List<Object[]> findAppaItemsNumberByCategory();
}
Podobnie jak to miało miejsce w przypadku zapytań własnych, tak samo w przypadku zapytań natywnych, podczas tworzenia takiego zapytania należy zwrócić uwagę na kilka aspektów:
  • Parametry wykorzystywane w tekście zapytania poprzedzamy dwukropkiem, np.: :lastName
  • Domyślnie parametry metody są przyporządkowane do parametrów z treści zapytania na podstawie kolejności miejsc, więc możemy NIE oznaczać parametrów metody za pomocą adnotacji @Param. Takie podejście bywa jednak problematyczne, w przypadku ewentualnych zmian w metodzie w przyszłości, szczególnie w przypadku refactoringu.
  • Stosujemy adnotację @Param, aby przyporządkować parametry metody do parametrów z treści zapytania na podstawie nazwy przekazanej do adnotacji. Nazwa musi zgadzać się z nazwą użytą w treści zapytania (po dwukropku). Nie jest wtedy wymagana zgodność co do kolejności parametrów.
  • W przypadku gdy chcemy dodać do zapytania opcje stronicowania Pageable lub sortowania Sort, dodajemy te typy na ostatniej pozycji wśród parametrów metody. Tutaj jedna uwaga. W przypadku użycia interfejsu do stronicowania w ramach zapytań natywnych, jesteśmy zobowiązani do podania zapytania zliczającego w ramach adnotacji @Query:
    public interface UserRepository extends JpaRepository<Item, Long> {
    
        @Query(value = "select * from users where lastName = :lastName /*#pageable*/ "
        		+ "order by lastName",
        countQuery = "select count(*) from users where lastName = :lastName",
        nativeQuery = true)
        Page findByLastname(String lastName, Pageable pageable);
    }
    
    
    Na koniec tego wątku musimy wspomnieć o bardzo ważnej rzeczy, która może się wydawać niejasna patrząc na powyższe zapytanie. Dotyczy ona zwrotu /*#pageable*/, który został dodany do zapytania jako workaround, aby takie zapytanie w ogóle zadziałało. Okazuje się bowiem, że Spring Data JPA przed wersją 2.x posiada błąd, który skutkuje tym, że podając w metodzie jako parametr interfejs Pageable nie jesteśmy w stanie takiego zapytania w ogóle uruchomić. Otrzymujemy błąd:
    Caused by: org.springframework.data.jpa.repository.query.
                                                InvalidJpaQueryMethodException:
    Cannot use native queries with dynamic sorting and/or pagination in method
        public abstract org.springframework.data.domain.Page
        com.javappa.startappa.appaimport.repository.UserRepository
        .findByLastname(java.lang.String,org.springframework.data.domain.Pageable)
        at org.springframework.data.jpa.repository.query.NativeJpaQuery.
        (NativeJpaQuery.java:55)
        at org.springframework.data.jpa.repository.query.JpaQueryFactory.
            fromMethodWithQueryString(JpaQueryFactory.java:72)
        at org.springframework.data.jpa.repository.query.JpaQueryFactory.
            fromQueryAnnotation(JpaQueryFactory.java:53)
    
    Jest to o tyle nielogiczne, że w oficjalnej dokumentacji Springa jest napisane, że o ile dynamiczne sortowanie nie jest wspierane, o tyle można użyć natywnych zapytań do paginacji poprzez podanie własnego zapytania COUNT oraz dodanie Pageable jako parametru metody. Okazuje się jednak, że z powodu błędnie zaimplementowanego kodu klasy NativeJpaQuery wymagane jest dodatkowo posiadanie wspomnianego tekstu w treści zapytania:
    public NativeJpaQuery(JpaQueryMethod method, EntityManager em, String queryString, 
        EvaluationContextProvider evaluationContextProvider,
                                                    SpelExpressionParser parser) {
       super(method, em, queryString, evaluationContextProvider, parser);
       JpaParameters parameters = method.getParameters();
       boolean hasPagingOrSortingParameter = parameters.hasPageableParameter() 
                        || parameters.hasSortParameter();
       boolean containsPageableOrSortInQueryExpression =
                                                    queryString.contains("#pageable")
                                                        || queryString.contains("#sort");
    
       if(hasPagingOrSortingParameter && !containsPageableOrSortInQueryExpression) {
           throw new InvalidJpaQueryMethodException("Cannot use native queries
                            with dynamic sorting and/or pagination in method " + method);
       }
    }
    
    Powyższy błąd został naprawiony przez programistów Springa dopiero w wersji 2.0.4 w marcu 2018 roku.

Zapytanie typu UPDATE lub DELETE

Podobnie jak to miało miejsce w przypadku zapytań własnych (Spring Data JPA - Zapytania własne), natywne zapytania modyfikujące również wymagają dodania adnotacji: @Modifying i @Transactional.
Rekomendacja
Zapytania natywne stanowią wyjątkowo efektywne narzędzie do rozwiązywania wielu problemów, jednak ważne jest, aby być świadomym pewnych ograniczeń, szczególnie w kontekście przenoszenia aplikacji między różnymi systemami zarządzania bazami danych. Wykorzystanie czystego SQL-a może prowadzić do wprowadzenia elementów specyficznych dla konkretnej bazy danych, na przykład Oracle, które są zgodne ze standardem SQL, ale niekoniecznie kompatybilne z innymi systemami, takimi jak PostgreSQL.

Taka sytuacja może skutkować tym, że po przeniesieniu aplikacji niektóre zapytania mogą nie działać poprawnie lub w ogóle przestać funkcjonować. Dlatego rekomendujemy ograniczenie stosowania zapytań natywnych do minimum albo przynajmniej zachowanie pełnej świadomości potencjalnych konsekwencji, jakie niesie za sobą to rozwiązanie.
Praktyka


Zapytania natywne są przez nas używane w kodzie pobierającym dane do wykresów w naszej aplikacji webowej oraz megapakiecie.
Zdjęcie autora
Autor: Jarek Klimas
Data: 03 stycznia 2024
Labele: Backend, Podstawowy, Java
W tej strefie znajdziesz wszystko co niezbędne, aby komfortowo uczyć się Hibernate'a. Doskonale opisany kod nie zawiera zbędnych komplikacji, tylko samą esencję w postaci praktycznych przykładów. Tutaj odnajdziesz wszystko co jest istotne w danym temacie. Otrzymujesz pakiet złożony z kilku projektów wraz z obszernym wytłumaczeniem kodu.
Topowe Materiały
Spring IO: JPA Query Methods
Baeldung: Spring Data JPA – Adding a Method in All Repositories

Udemy: [NEW] Spring Boot 3, Spring 6 & Hibernate for Beginners  —  polskie napisy

Stale się rozwijamy, a więc bądź na bieżąco!
Na ten adres będziemy przesyłać informacje o ważniejszych aktualizacjach, a także o nowych materiałach pojawiających się na stronie.
Polub nas na Facebooku:
Nasi partnerzy: stackshare
Javappa to również profesjonalne usługi programistyczne oparte o technologie JAVA. Jeśli chesz nawiązać z nami kontakt w celu uzyskania doradztwa bądź stworzenia aplikacji webowej powinieneś poznać nasze doświadczenia.
Kliknij O nas .


Pozycjonowanie stron: Grupa TENSE