Spring Data JPA - Zapytania własne

Podczas tworzenie kodu aplikacji czasem zdarza się tak, że nie chcemy, albo z jakiegoś powodu nie możemy używać rozwiązań dostarczanych out of the box przez framework. Tak może też być na przykład w przypadku tworzenia zapytań i właśnie o tym chcemy napisać w bieżącym rozdziale.

Zapytanie pobierające typu SELECT

Spójrzmy na początek na przykład pobierający dane z bazy. Widzimy, że zapytanie jest odpowiedzialne za wyszukiwanie rekordów przy pomocy klauzuli LIKE, mającej za zadanie porównywać ciągi tekstowe przekazywane do metody z zewnątrz. Mamy tutaj również dodatkowe funkcje operujące na datach oraz rzutowanie id na stringa. Treść zapytania została przekazana frameworkowi do wykonania poprzez użycie adnotacji @Query.
public interface ItemRepository extends JpaRepository<Item, Long> {

    @Query("select ai from Item ai where ai.name LIKE :searchValue "
    		+ "OR cast(ai.id as string) LIKE :searchValue "
    		+ "OR FORMATDATETIME(ai.dateTimeFrom, 'dd/MM/yyyy') LIKE :searchValue "
    		+ "OR FORMATDATETIME(ai.dateTimeTo, 'dd/MM/yyyy') LIKE :searchValue")
    Page<Item> findAll(@Param("searchValue") String searchValue, Pageable pageable);

    ...
}

Teraz kilka uwag dotyczących tego na co należy zwrócić uwagę podczas tworzenia takiego zapytania:
  • Parametry wykorzystywane w tekście zapytania poprzedzamy dwukropkiem: :searchValue
  • 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. Jeśli potrzebujemy oba, bedą zajmowały one ostatnie dwie pozycje.

Zapytanie modyfikujące typu UPDATE lub DELETE

Załóżmy, że potrzebujemy napisać własne zapytanie operujące na JPA, którego zadaniem będzie aktualizacja danych w systemie, w przypadku gdy zostanie spełniony określony warunek. Napiszemy więc zapytanie z użyciem adnotacji @Query, podobnie jak to robiliśmy dla zapytania pobierającego:
public interface UserRepository extends JpaRepository<User, Long> {

    ...

    static final String UPDATE_EXPIRED_TOKENS = "UPDATE User u SET u.token = null "
    		+ "WHERE u.token is NOT NULL AND u.tokenTime < :tokenTime";
    
    @Modifying
    @Transactional
    @Query(UPDATE_EXPIRED_TOKENS)
    int updateExpiredTokens(@Param("tokenTime") Date tokenTime);
}
Zapytanie jest odpowiedzialne za wyczyszczenie wszystkich przedawnionych tokenów, czyli de facto dla wszystkich użytkowników, którzy nie zdążyli użyć wygenerowanego tokena przed upływem określonego czasu. Patrząc na kod zauważamy kilka interesujących rzeczy:
  • Zapytanie możemy zdefiniować jako stałą w kodzie i użyć go jako parametr adnotacji @Query
  • Zapytanie modyfikujące musi być oznaczone adnotacją @Modifying
  • Zapytanie modyfikujące musi być wykonane w ramach aktywnej transakcji, co osiągamy wprowadzając adnotację @Transactional
Podobnie będzie to wyglądało podczas tworzenia zapytania typu DELETE.

W tym miejscu zapraszamy Was na naszego bloga, na którym opisujemy jeszcze dokładniej przykład zapytania modyfikującego. Tam też istnieje możliwość ściągnięcia gotowego kodu w postaci zip-a z krótkim programem Spring Boot w środku:

Spring Data Custom Query - Modifying & Transactional
Nasza rekomendacja
W kursach stosujemy to zmiennie, ale generalnie dobrze jest używać stałych do przechowywania zapytań - tak jak to pokazaliśmy w ostatnim przykładzie. Jest to szczególnie przydatne, gdy w naszym interfejsie nagromadzi się duża ilość zapytań. Kod wygląda wtedy na bardziej uporządkowany, co zwiększa jego przejrzystość.

Nie ukrywamy, że zapytania własne są rozwiązaniem, które bardzo nam się podoba, gdyż zwiększa to elastyczność kodu aplikacji. Wykonując zawód programisty musimy być przygotowani na ciągłe zmiany, które będą się pojawiały w przyszłości w naszym kodzie i to zmiany które raczej będą szły w kierunku zwiększania złożoności tego kodu. Stąd też może się okazać, że z czasem zapytanie wbudowane (Spring Data JPA - Zapytania wbudowane), stanie się niewystarczające do tego, aby wykonać pewne operacje i będziemy wtedy zmuszeni do użycia alternatywy w postaci stworzenia własnego zapytania.

Czasem nawet i to jest ciągle za mało, dlatego też Spring Data JPA udostępnia jeszcze jedną, "luźniejszą" formę budowania zapytań, w postaci zapytań natywnych (czysty SQL), o których przeczytacie w kolejnym rozdziale.
Używamy w StartAPPa


Przykłady przytoczone w tym rozdziale pochodzą z kursów:

Zapytanie SELECT - Tabela Zaawansowana (część dotycząca filtrowania rezultatów pokazywanych w tabeli)
Zapytanie UPDATE - Login & Reset (część dotycząca procesu resetowania hasła przez użytkownika)
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.
Linki
https://docs.spring.io/spring-data/jpa/docs/1.11.16.RELEASE/reference/html/#jpa.query-methods.at-query

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:
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 .