Spring Data JPA - Zapytania Własne

W procesie tworzenia kodu aplikacji, zdarzają się sytuacje, gdy z różnych powodów nie jest możliwe lub preferowane korzystanie z gotowych rozwiązań oferowanych przez framework. Ta kwestia może dotyczyć również tworzenia zapytań, co jest głównym tematem niniejszego rozdziału.

Zapytanie typu SELECT

Rozpocznijmy od analizy przykładu, który ilustruje pobieranie danych z bazy danych. Zauważ, że zastosowane zapytanie wykorzystuje klauzulę LIKE, która służy do porównywania ciągów tekstowych przekazanych metodzie z zewnątrz. Zapytanie to zawiera również dodatkowe funkcjonalności manipulujące datami oraz konwersję identyfikatora na typ String. Całość zapytania jest przekazywana do frameworka do wykonania za pomocą 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, będą zajmowały one ostatnie dwie pozycje.

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

Rekomendacja
Dobrą praktyką jest używanie stałych do przechowywania zapytań, co zostało zilustrowane w ostatnim przykładzie. Jest to szczególnie korzystne w przypadku, gdy interfejs zawiera liczne zapytania. Dzięki temu kod jest bardziej uporządkowany, co przekłada się na jego lepszą czytelność.

Jako programiści musimy być gotowi na nieustanne zmiany i rozwój kodu, które często prowadzą do zwiększenia jego złożoności. W związku z tym, istnieje możliwość, że w przyszłości zapytania wbudowane (Spring Data JPA - Zapytania wbudowane) okażą się niewystarczające do realizacji niektórych operacji, co skłoni nas do stworzenia omawianego właśnie zapytania własnego (Custom Query).

W niektórych przypadkach, nawet zapytania własne mogą nie spełniać wszystkich wymagań. Dlatego Spring Data JPA oferuje jeszcze inną metodę tworzenia zapytań, mianowicie zapytania natywne (czysty SQL), które zostaną omówione w kolejnym rozdziale.
Praktyka


Przykłady przytoczone w tym rozdziale pochodzą z aplikacji z naszego mega pakietu.
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