Spring Data JPA @Query

1. Pregled

Spring Data pruža mnogo načina za definiranje upita koji možemo izvršiti. Jedan od tih je @Query bilješka.

U ovom uputstvu ćemo pokazati kako koristiti @Query napomena u Spring Data JPA za izvršavanje JPQL i izvornih SQL upita.

Pokazat ćemo i kako izraditi dinamički upit kada @Query anotacija nije dovoljna.

2. Odaberite Upit

Da bismo definirali SQL za izvršavanje za metodu spremišta podataka Spring, možemo označiti metodu s @Query napomena - njegova vrijednost atribut sadrži JPQL ili SQL za izvršenje.

The @Query bilješka ima prednost nad imenovanim upitima koji su označeni s @NamedQuery ili definirano u orm.xml datoteka.

Dobar je pristup postaviti definiciju upita odmah iznad metode unutar spremišta, a ne unutar našeg modela domene kao imenovani upiti. Spremište je odgovorno za postojanost, pa je bolje mjesto za pohranu ovih definicija.

2.1. JPQL

Prema zadanim postavkama definicija upita koristi JPQL.

Pogledajmo jednostavnu metodu spremišta koja vraća aktivnu Korisnik entiteti iz baze podataka:

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

2.2. Native

Za definiranje upita možemo koristiti i izvorni SQL. Sve što moramo učiniti je postavite vrijednost nativeQuery pripisati pravi i definirajte izvorni SQL upit u vrijednost atribut bilješke:

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

3. Definirajte redoslijed u upitu

Možemo proslijediti dodatni parametar tipa Vrsta deklaraciji metode Spring Data koja ima @Query bilješka. Prevest će se u NARUČITE PO klauzula koja se prenosi u bazu podataka.

3.1. Sortiranje prema JPA metodama i izvedenim metodama

Za metode koje izvlačimo iz okvira kao što su findAll (Poredaj) ili oni koji se generiraju potpisima metode raščlanjivanja, svojstva objekta možemo koristiti samo za definiranje svoje sorte:

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

Sad zamislite da želimo sortirati po duljini svojstva imena:

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

Kada izvršimo gornji kod, primit ćemo iznimku:

org.springframework.data.mapping.PropertyReferenceException: Nije pronađeno svojstvo LENGTH (ime) za tip Korisnik!

3.2. JPQL

Kada koristimo JPQL za definiciju upita, tada Spring Data bez problema može podnijeti sortiranje - sve što moramo učiniti je dodati parametar tipa tipa Vrsta:

@Query (value = "SELECT u FROM User u") Popis findAllUsers (Poredaj sortiranje); 

Možemo nazvati ovu metodu i proslijediti a Vrsta parametar, koji će poredati rezultat po Ime vlasništvo Korisnik objekt:

userRepository.findAllUsers (novo Sort ("ime"));

I zato što smo koristili @Query napomena, možemo koristiti istu metodu za dobivanje razvrstanog popisa Korisnici prema duljini njihovih imena:

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

Ključno je da se koristimo JpaSort.unsafe () stvoriti a Vrsta instanca objekta.

Kada koristimo:

new Sort ("LENGTH (name)"); 

tada ćemo dobiti potpuno istu iznimku kao što smo vidjeli gore za findAll () metoda.

Kad Spring Data otkrije nesigurno Vrsta narudžba za metodu koja koristi @Query napomena, a zatim samo dodaje klauzulu sortiranja na upit - preskače provjeru pripada li svojstvo po kojem pripada modelu domene.

3.3. Native

Kada @Query napomena koristi izvorni SQL, tada nije moguće definirati Vrsta.

Ako to učinimo, primit ćemo iznimku:

org.springframework.data.jpa.repository.query.InvalidJpaQueryMethodException: Ne mogu se koristiti izvorni upiti s dinamičkim sortiranjem i / ili paginacijom

Kao što kaže iznimka, sortiranje nije podržano za nativne upite. Poruka pogreške daje nam naslutiti da će i paginacija izazvati iznimku.

Međutim, postoji zaobilazno rješenje koje omogućuje paginaciju, a mi ćemo ga pokriti u sljedećem odjeljku.

4. Paginacija

Paginacija nam omogućuje da u a. Vratimo samo podskup cijelog rezultata Stranica. To je korisno, na primjer, kada se krećete kroz nekoliko stranica podataka na web stranici.

Još jedna prednost paginacije je u tome što je količina podataka poslanih s poslužitelja na klijenta svedena na minimum. Slanjem manjih dijelova podataka općenito možemo vidjeti poboljšanje performansi.

4.1. JPQL

Korištenje paginacije u definiciji upita JPQL jednostavno je:

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

Možemo proći a Zahtjev za stranicu parametar za dobivanje stranice podataka.

Paginacija je također podržana za nativne upite, ali zahtijeva malo dodatnog rada.

4.2. Native

Možemo omogućiti paginaciju za izvorne upite deklariranjem dodatnog atributa countQuery.

Ovo definira SQL koji će se izvršiti za brojanje broja redaka u cijelom rezultatu:

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

4.3. Proljetne podatke JPA verzije prije 2.0.4

Gornje rješenje za izvorne upite izvrsno funkcionira za Spring Data JPA verzije 2.0.4 i novije.

Prije te verzije, kad pokušamo izvršiti takav upit, primit ćemo istu iznimku koju smo opisali u prethodnom odjeljku o sortiranju.

To možemo prevladati dodavanjem dodatnog parametra za paginiranje unutar našeg upita:

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

U gornjem primjeru dodajemo “\ n– #pageable \ n” kao rezervirano mjesto za paginacijski parametar. Ovo govori Spring Data JPA kako raščlaniti upit i ubrizgati parametar koji se može stranicati. Ovo rješenje djeluje za H2 baza podataka.

Opisali smo kako stvoriti jednostavne upite za odabir putem JPQL-a i nativnog SQL-a. Zatim ćemo pokazati kako definirati dodatne parametre.

5. Indeksirani parametri upita

Postoje dva moguća načina na koje možemo proslijediti parametre metode našem upitu: indeksirani i imenovani parametri.

U ovom ćemo odjeljku pokriti indeksirane parametre.

5.1. JPQL

Za indeksirane parametre u JPQL-u, Spring Data će prosljeđuju parametre metode upitu istim redoslijedom kojim se pojavljuju u deklaraciji metode:

@Query ("SELECT u FROM User u WHERE u.status =? 1") User findUserByStatus (status cijelog broja); @Query ("SELECT u FROM User u WHERE u.status =? 1 and u.name =? 2") Korisnik findUserByStatusAndName (status cijelog broja, naziv niza); 

Za gornje upite, status Parametar metode bit će dodijeljen parametru upita s indeksom 1, i Ime Parametar metode bit će dodijeljen parametru upita s indeksom 2.

5.2. Native

Indeksirani parametri za izvorne upite rade točno na isti način kao i za JPQL:

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

U sljedećem odjeljku prikazat ćemo drugačiji pristup: prosljeđivanje parametara putem imena.

6. Imenovani parametri

Možemo i mi proslijediti parametre metode na upit pomoću imenovanih parametara. Mi ih definiramo pomoću @Param napomena unutar naše deklaracije metode spremišta.

Svaki parametar označen s @Param mora imati niz vrijednosti koji odgovara odgovarajućem nazivu parametra upita JPQL ili SQL. Upit s imenovanim parametrima lakše je čitati i manje je podložan pogreškama u slučaju da upit treba refaktorirati.

6.1. JPQL

Kao što je gore spomenuto, koristimo @Param napomena u deklaraciji metode kako bi se podudarali parametri definirani imenom u JPQL-u s parametrima iz deklaracije metode:

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

Imajte na umu da smo u gornjem primjeru definirali SQL upit i parametre metode da imaju ista imena, ali nije potrebno sve dok su nizovi vrijednosti isti:

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

6.2. Native

Za definiciju izvornog upita nema razlike u načinu na koji prenosimo parametar kroz ime na upit u odnosu na JPQL - koristimo @Param napomena:

@Query (value = "SELECT * FROM Users u WHERE u.status =: status and u.name =: name", nativeQuery = true) Korisnik findUserByStatusAndNameNamedParamsNative (@Param ("status") Status cijelog broja, @Param ("name" ) Naziv niza);

7. Parametar sakupljanja

Razmotrimo slučaj kada gdje klauzula našeg JPQL ili SQL upita sadrži U (ili NE U) ključna riječ:

ODABERI OD FUNER korisnika u GDJE u.ime IN: imena

U ovom slučaju možemo definirati metodu upita koja traje Kolekcija kao parametar:

@Query (value = "SELECT u FROM User u WHERE u.name IN: names") Popis findUserByNameList (@Param ("imena") Imena zbirki);

Kako je parametar a Kolekcija, može se koristiti sa Popis, HashSetitd.

Dalje ćemo pokazati kako mijenjati podatke pomoću @Modificiranje bilješka.

8. Ažuriranje upita pomoću @Modificiranje

Možemo koristiti @Upit napomena za izmjenu stanja baze podataka dodavanjem također znaka @Modificiranje bilješka na metodu spremišta.

8.1. JPQL

Metoda spremišta koja modificira podatke ima dvije razlike u usporedbi s Odaberi upit - ima @Modificiranje napomena i, naravno, koristi se JPQL upit ažuriranje umjesto Odaberi:

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

Povratna vrijednost definira koliko je redaka ažurirano izvršenje upita. I indeksirani i imenovani parametri mogu se koristiti unutar upita za ažuriranje.

8.2. Native

Stanje baze podataka možemo izmijeniti i pomoću izvornog upita. Samo trebamo dodati @Modificiranje napomena:

@Modificiranje @Query (value = "update korisnika u postaviti u.status =? Where u.name =?", NativeQuery = true) int updateUserSetStatusForNameNative (status cijelog broja, naziv niza);

8.3. Umetci

Da bismo izvršili operaciju umetanja, moramo se prijaviti @Modificiranje i koristite izvorni upit jer INSERT nije dio JPA sučelja:

@Modifying @Query (value = "umetni u Korisnici (ime, dob, e-pošta, status) vrijednosti (: name,: age,: email,: status)", nativeQuery = true) void insertUser (@Param ("name") Ime niza, @Param ("dob") Cijela dob, @Param ("status") Status cijelog broja, @Param ("e-pošta") Niz e-pošte);

9. Dinamički upit

Često ćemo naići na potrebu za izgradnjom SQL izraza na temelju uvjeta ili skupova podataka čije su vrijednosti poznate samo u vrijeme izvođenja. I u tim slučajevima ne možemo koristiti samo statički upit.

9.1. Primjer dinamičkog upita

Na primjer, zamislimo situaciju u kojoj trebamo odabrati sve korisnike čija je e-pošta KAO jedan iz skupa definiranog u vrijeme izvođenja - e-pošta1, e-adresa2, …, emailn:

ODABERITE OD korisnika u GDJE u.email KAO '% email1%' ili u.email KAO '% email2%' ... ili u.email KAO '% emailn%'

Budući da je skup dinamički konstruiran, u vrijeme sastavljanja ne možemo znati koliko KAO klauzule za dodavanje.

U ovom slučaju, ne možemo samo koristiti @Query napomena jer ne možemo pružiti statički SQL izraz.

Umjesto toga, primjenom prilagođenog kompozitnog spremišta, možemo proširiti bazu JpaRepository funkcionalnost i pružamo vlastitu logiku za izgradnju dinamičkog upita. Pogledajmo kako to učiniti.

9.2. Prilagođena spremišta i API kriterija JPA

Srećom po nas, Spring pruža način za proširenje osnovnog spremišta korištenjem prilagođenih sučelja fragmenata. Tada ih možemo povezati kako bismo stvorili složeno spremište.

Počet ćemo izradom prilagođenog sučelja za fragmente:

javno sučelje UserRepositoryCustom {Lista findUserByEmails (Postavi e-poštu); }

A onda ćemo to implementirati:

javna klasa UserRepositoryCustomImpl implementira UserRepositoryCustom {@PersistenceContext private EntityManager entityManager; @Preuzmi javni popis findUserByEmails (Postavi e-poštu) {CriteriaBuilder cb = entityManager.getCriteriaBuilder (); CriteriaQuery query = cb.createQuery (User.class); Korijenski korisnik = query.from (User.class); Putanja emailPath = user.get ("email"); Popis predikata = novi ArrayList (); for (String email: e-mailovi) {predikati.add (cb.like (emailPath, email)); } query.select (user) .where (cb.or (preicates.toArray (novi predikat [predikati.size ()]))); vratiti entityManager.createQuery (upit) .getResultList (); }}

Kao što je gore prikazano, iskoristili smo API JPA kriterija za izgradnju našeg dinamičkog upita.

Također, moramo osigurati da uključimo Impl postfix u nazivu klase. Proljeće će pretražiti UserRepositoryCustom provedba kao UserRepositoryCustomImpl. Budući da fragmenti sami po sebi nisu spremišta, Spring se oslanja na ovaj mehanizam kako bi pronašao implementaciju fragmenta.

9.3. Proširenje postojećeg spremišta

Primijetite da su sve metode upita od odjeljka 2 do odjeljka 7 u UserRepository.

Dakle, sada ćemo integrirati naš fragment proširujući novo sučelje u UserRepository:

javno sučelje UserRepository proširuje JpaRepository, UserRepositoryCustom {// metode upita iz odjeljka 2 - odjeljka 7}

9.4. Korištenje spremišta

I na kraju, možemo nazvati našu metodu dinamičkog upita:

Postavi e-poštu = novi HashSet (); // punjenje skupa s bilo kojim brojem predmeta userRepository.findUserByEmails (e-mailovi); 

Uspješno smo stvorili kompozitno spremište i pozvali našu prilagođenu metodu.

10. Zaključak

U ovom smo članku pokrili nekoliko načina definiranja upita u metodama spremišta Spring Data JPA pomoću @Query bilješka.

Također smo naučili kako implementirati prilagođeno spremište i stvoriti dinamički upit.

Kao i uvijek, cjeloviti primjeri koda korišteni u ovom članku dostupni su na GitHubu.