Spring Data JPA @Query

Prezentare generală

Spring Data oferă multe modalități de a defini o interogare pe care o putem executa. Una dintre acestea este adnotarea @Query.

în acest tutorial, vom demonstra cum să utilizați adnotarea @Query în Spring Data JPA pentru a executa atât interogări JPQL, cât și interogări SQL native.

de asemenea, vom arăta cum să construim o interogare dinamică atunci când adnotarea @Query nu este suficientă.

lecturi suplimentare:

metode de interogare derivate în depozitele Spring Data JPA

explorați mecanismul de derivare a interogării în Spring Data JPA.
Citeste mai mult →

Spring Data JPA @ modificarea adnotării

creați interogări DML și DDL în Spring Data JPA prin combinarea @ Query și @ modificarea adnotărilor
Citeste mai mult →

selectați Query

pentru a defini SQL de executat pentru o metodă de depozit de date Spring, putem adnota metoda cu adnotarea @Query — atributul său de valoare conține JPQL sau SQL de executat.

adnotarea @Query are prioritate față de interogările numite, care sunt adnotate cu @NamedQuery sau definite într-un orm.fișier xml.

este o abordare bună pentru a plasa o definiție de interogare chiar deasupra metodei în interiorul depozitului, mai degrabă decât în interiorul modelului nostru de domeniu ca interogări numite. Depozitul este responsabil pentru persistență, deci este un loc mai bun pentru a stoca aceste definiții.

2.1. JPQL

în mod implicit, definiția interogării utilizează JPQL.

să ne uităm la o metodă simplă depozit care returnează entități utilizator activ din Baza de date:

@Query("SELECT u FROM User u WHERE u.status = 1")Collection<User> findAllActiveUsers();

2.2. Nativ

putem folosi, de asemenea, SQL nativ pentru a defini interogarea noastră. Tot ce trebuie să facem este să setăm valoarea atributului nativeQuery la true și să definim interogarea SQL nativă în atributul value al adnotării:

@Query( value = "SELECT * FROM USERS u WHERE u.status = 1", nativeQuery = true)Collection<User> findAllActiveUsersNative();

definiți ordinea într-o interogare

putem trece un parametru suplimentar de sortare de tip la o declarație de metodă de date de primăvară care are adnotarea @Query. Acesta va fi tradus în ordinea de clauza care devine trecut la baza de date.

3.1. Sortare pentru metodele JPA furnizate și derivate

pentru metodele pe care le scoatem din cutie, cum ar fi findAll(sortare) sau cele care sunt generate prin analiza semnăturilor metodei, putem folosi doar proprietățile obiectului pentru a defini sortarea noastră:

userRepository.findAll(Sort.by(Sort.Direction.ASC, "name"));

acum imaginați-vă că vrem să sortăm după lungimea unei proprietăți de nume:

userRepository.findAll(Sort.by("LENGTH(name)"));

când executăm codul de mai sus, vom primi o excepție:

org.springframe.data.cartografiere.PropertyReferenceException: nici o lungime de proprietate(nume) găsit pentru utilizator de tip!

3.2. JPQL

când folosim JPQL pentru o definiție de interogare, atunci Spring Data se poate ocupa de sortare fără nici o problemă — tot ce trebuie să facem este să adăugăm un parametru metodă de tip Sortare:

@Query(value = "SELECT u FROM User u")List<User> findAllUsers(Sort sort);

putem apela această metodă și să trecem un parametru de sortare, care va ordona rezultatul prin proprietatea de nume a obiectului utilizator:

userRepository.findAllUsers(Sort.by("name"));

și pentru că am folosit adnotarea @ Query, putem folosi aceeași metodă pentru a obține lista sortată a utilizatorilor după lungimea numelor lor:

userRepository.findAllUsers(JpaSort.unsafe("LENGTH(name)"));

este crucial să folosim JpaSort.nesigur () pentru a crea o instanță obiect Sortare.

când folosim:

Sort.by("LENGTH(name)");

atunci vom primi exact aceeași excepție pe care am văzut-o mai sus pentru metoda findAll ().

când Spring Data descoperă ordinea de sortare nesigură pentru o metodă care utilizează adnotarea @Query, atunci doar adaugă clauza sort la interogare — ignoră verificarea dacă proprietatea de sortare aparține modelului de domeniu.

3.3. Nativ

când adnotarea @Query utilizează SQL nativ, atunci nu este posibil să se definească un fel.

dacă o facem, vom primi o excepție:

org.springframe.data.jpa.depozit.interogare.InvalidJpaQueryMethodException: nu se poate utiliza interogări native cu sortare dinamică și/sau paginare

după cum spune excepția, sortarea nu este acceptată pentru interogări native. Mesajul de eroare ne oferă un indiciu că paginarea va provoca și o excepție.

cu toate acestea, există o soluție care permite paginarea și o vom acoperi în secțiunea următoare.

paginare

paginare ne permite să se întoarcă doar un subset de un rezultat întreg într-o pagină. Acest lucru este util, de exemplu, atunci când navigați prin mai multe pagini de date de pe o pagină web.

un alt avantaj al paginării este că cantitatea de date trimise de la server la client este minimizată. Prin trimiterea de date mai mici, putem vedea, în general, o îmbunătățire a performanței.

4.1. JPQL

utilizarea paginării în definiția interogării JPQL este simplă:

@Query(value = "SELECT u FROM User u ORDER BY id")Page<User> findAllUsersWithPagination(Pageable pageable);

putem trece un parametru PageRequest pentru a obține o pagină de date.

paginarea este de asemenea acceptată pentru interogări native, dar necesită puțină muncă suplimentară.

4.2. Nativ

putem activa paginarea pentru interogări native prin declararea unui atribut suplimentar countQuery.

aceasta definește SQL pentru a executa pentru a număra numărul de rânduri din întregul rezultat:

@Query( value = "SELECT * FROM Users ORDER BY id", countQuery = "SELECT count(*) FROM Users", nativeQuery = true)Page<User> findAllUsersWithPagination(Pageable pageable);

4.3. Spring Data JPA versiuni anterioare 2.0.4

soluția de mai sus pentru interogări native funcționează bine pentru Spring Data JPA versiunile 2.0.4 și mai târziu.

înainte de această versiune, când încercăm să executăm o astfel de interogare, vom primi aceeași excepție pe care am descris-o în secțiunea anterioară despre sortare.

putem depăși acest lucru adăugând un parametru suplimentar pentru paginare în interogarea noastră:

@Query( value = "SELECT * FROM Users ORDER BY id \n-- #pageable\n", countQuery = "SELECT count(*) FROM Users", nativeQuery = true)Page<User> findAllUsersWithPagination(Pageable pageable);

în exemplul de mai sus, adăugăm „\n– #pageable\n” ca substituent pentru parametrul paginare. Aceasta spune Spring Data JPA cum să analizeze interogarea și să injecteze parametrul pageable. Această soluție funcționează pentru baza de date H2.

am acoperit cum să creați interogări simple de selectare prin JPQL și SQL nativ. Apoi, vom arăta cum să definiți parametri suplimentari.

parametrii de interogare indexați

există două moduri posibile prin care putem trece parametrii metodei la interogarea noastră: parametrii indexați și denumiți.

în această secțiune, vom acoperi parametrii indexați.

5.1. JPQL

pentru parametrii indexați în JPQL, datele de primăvară vor trece parametrii metodei la interogare în aceeași ordine în care apar în declarația metodei:

@Query("SELECT u FROM User u WHERE u.status = ?1")User findUserByStatus(Integer status);@Query("SELECT u FROM User u WHERE u.status = ?1 and u.name = ?2")User findUserByStatusAndName(Integer status, String name);

pentru interogările de mai sus, parametrul metodei de stare va fi atribuit parametrului de interogare cu indexul 1, iar parametrul metodei de nume va fi atribuit parametrului de interogare cu indexul 2.

5.2. Nativ

parametrii indexați pentru interogările native funcționează exact în același mod ca și pentru JPQL:

@Query( value = "SELECT * FROM Users u WHERE u.status = ?1", nativeQuery = true)User findUserByStatusNative(Integer status);

în secțiunea următoare, vom arăta o abordare diferită: trecerea parametrilor prin nume.

parametrii denumiți

de asemenea, putem trece parametrii metodei la interogare folosind parametrii numiți. Le definim folosind adnotarea @ Param din declarația metodei depozitului nostru.

fiecare parametru adnotat cu @Param trebuie să aibă un șir de valori care să corespundă numelui parametrului de interogare JPQL sau SQL corespunzător. O interogare cu parametri numiți este mai ușor de citit și este mai puțin predispusă la erori în cazul în care interogarea trebuie refactorizată.

6.1. JPQL

după cum sa menționat mai sus, folosim adnotarea @ Param în declarația metodei pentru a potrivi parametrii definiți de nume în JPQL cu parametrii din declarația metodei:

@Query("SELECT u FROM User u WHERE u.status = :status and u.name = :name")User findUserByStatusAndNameNamedParams( @Param("status") Integer status, @Param("name") String name);

rețineți că în exemplul de mai sus, am definit parametrii de interogare și metodă SQL pentru a avea aceleași nume, dar nu este necesar atâta timp cât șirurile de valori sunt aceleași:

@Query("SELECT u FROM User u WHERE u.status = :status and u.name = :name")User findUserByUserStatusAndUserName(@Param("status") Integer userStatus, @Param("name") String userName);

6.2. Nativ

pentru definiția interogării native, nu există nicio diferență în modul în care transmitem un parametru prin numele interogării în comparație cu JPQL — folosim adnotarea @ Param:

@Query(value = "SELECT * FROM Users u WHERE u.status = :status and u.name = :name", nativeQuery = true)User findUserByStatusAndNameNamedParamsNative( @Param("status") Integer status, @Param("name") String name);

7. Parametrul de colectare

să luăm în considerare cazul în care clauza where a interogării noastre JPQL sau SQL conține cuvântul cheie IN (sau NOT in) :

SELECT u FROM User u WHERE u.name IN :names

în acest caz, putem defini o metodă de interogare care ia colecția ca parametru:

@Query(value = "SELECT u FROM User u WHERE u.name IN :names")List<User> findUserByNameList(@Param("names") Collection<String> names);

deoarece parametrul este o colecție, acesta poate fi utilizat cu Listă, HashSet etc.

în continuare, vom arăta cum să modificați datele cu adnotarea @modificatoare.

actualizați interogările cu @Modificing

putem folosi adnotarea @Query pentru a modifica starea bazei de date adăugând și adnotarea @Modificing la metoda repository.

8.1. JPQL

metoda depozitului care modifică datele are două diferențe în comparație cu interogarea select — are adnotarea @ modificatoare și, desigur, interogarea JPQL folosește actualizare în loc de selectare:

@Modifying@Query("update User u set u.status = :status where u.name = :name")int updateUserSetStatusForName(@Param("status") Integer status, @Param("name") String name);

valoarea returnată definește câte rânduri a fost actualizată execuția interogării. Atât parametrii indexați, cât și cei denumiți pot fi utilizați în interogările de actualizare.

8.2. Nativ

putem modifica starea bazei de date, de asemenea, cu o interogare nativ. Trebuie doar să adăugăm adnotarea @ modificatoare:

@Modifying@Query(value = "update Users u set u.status = ? where u.name = ?", nativeQuery = true)int updateUserSetStatusForNameNative(Integer status, String name);

8.3. Inserturi

pentru a efectua o operație de inserare, trebuie să aplicăm @Modifying și să folosim o interogare nativă, deoarece INSERT nu face parte din interfața JPA:

@Modifying@Query( value = "insert into Users (name, age, email, status) values (:name, :age, :email, :status)", nativeQuery = true)void insertUser(@Param("name") String name, @Param("age") Integer age, @Param("status") Integer status, @Param("email") String email);

interogare dinamică

adesea, vom întâmpina nevoia de a construi instrucțiuni SQL bazate pe condiții sau seturi de date ale căror valori sunt cunoscute doar în timpul rulării. Și în aceste cazuri, nu putem folosi doar o interogare statică.

9.1. Exemplu de interogare dinamică

de exemplu, să ne imaginăm o situație în care trebuie să selectăm toți utilizatorii al căror e-mail este ca unul dintr-un set definit în timpul rulării-email1, email2,…, emailn:

SELECT u FROM User u WHERE u.email LIKE '%email1%' or u.email LIKE '%email2%' ... or u.email LIKE '%emailn%'

deoarece setul este construit dinamic, nu putem ști la compilare câte clauze similare să adăugăm.

în acest caz, nu putem folosi adnotarea @Query, deoarece nu putem furniza o instrucțiune SQL statică.

în schimb, prin implementarea unui depozit compozit personalizat, putem extinde funcționalitatea JpaRepository de bază și putem oferi propria noastră logică pentru construirea unei interogări dinamice. Să aruncăm o privire la modul de a face acest lucru.

9.2. Depozite personalizate și criteriile JPA API

din fericire pentru noi, Spring oferă o modalitate de extindere a depozitului de bază prin utilizarea interfețelor fragment personalizate. Apoi le putem lega împreună pentru a crea un depozit compozit.

vom începe prin crearea unei interfețe fragment personalizat:

public interface UserRepositoryCustom { List<User> findUserByEmails(Set<String> emails);}

și apoi o vom implementa:

public class UserRepositoryCustomImpl implements UserRepositoryCustom { @PersistenceContext private EntityManager entityManager; @Override public List<User> findUserByEmails(Set<String> emails) { CriteriaBuilder cb = entityManager.getCriteriaBuilder(); CriteriaQuery<User> query = cb.createQuery(User.class); Root<User> user = query.from(User.class); Path<String> emailPath = user.get("email"); List<Predicate> predicates = new ArrayList<>(); for (String email : emails) { predicates.add(cb.like(emailPath, email)); } query.select(user) .where(cb.or(predicates.toArray(new Predicate))); return entityManager.createQuery(query) .getResultList(); }}

după cum se arată mai sus, am folosit API-ul JPA Criteria pentru a construi interogarea noastră dinamică.

de asemenea, trebuie să ne asigurăm că includem Postfix Impl în numele clasei. Spring va căuta implementarea UserRepositoryCustom ca UserRepositoryCustomImpl. Deoarece fragmentele nu sunt depozite de la sine, Spring se bazează pe acest mecanism pentru a găsi implementarea fragmentului.

9.3. Extinderea magaziei existente

observați că toate metodele de interogare de la Secțiunea 2 până la Secțiunea 7 se află în UserRepository.

Deci, acum vom integra fragmentul nostru prin extinderea noii interfețe în UserRepository:

public interface UserRepository extends JpaRepository<User, Integer>, UserRepositoryCustom { // query methods from section 2 - section 7}

9.4. Folosind depozitul

și, în final, putem apela metoda noastră de interogare dinamică:

Set<String> emails = new HashSet<>();// filling the set with any number of itemsuserRepository.findUserByEmails(emails);

am creat cu succes un depozit compozit și am numit metoda noastră personalizată.

concluzie

în acest articol, am acoperit mai multe moduri de definire a interogărilor în metodele de depozitare Spring Data JPA folosind adnotarea @Query.

de asemenea, am învățat cum să implementăm un depozit personalizat și să creăm o interogare dinamică.

ca întotdeauna, exemplele complete de cod utilizate în acest articol sunt disponibile pe GitHub.

Lasă un răspuns

Adresa ta de email nu va fi publicată.