Spring Data JPA @Query

1. Oversikt

Spring Data gir mange måter å definere et spørsmål vi kan utføre. En av disse er @Spørsmål kommentar.

I denne opplæringen vil vi demonstrere hvordan du bruker @Spørsmål kommentar i Spring Data JPA for å utføre både JPQL og native SQL-spørringer.

Vi viser også hvordan du bygger et dynamisk spørsmål når @Spørsmål kommentar er ikke nok.

2. Velg Spørring

For å definere SQL som skal kjøres for en Spring Data repository-metode, kan vi kommentere metoden med @Spørsmål kommentar - dens verdi attributt inneholder JPQL eller SQL å utføre.

De @Spørsmål merknader går foran navngitte søk, som er merket med @NamedQuery eller definert i en orm.xml fil.

Det er en god tilnærming å plassere en spørringsdefinisjon rett over metoden i depotet i stedet for i vår domenemodell som navngitte spørsmål. Datalageret er ansvarlig for utholdenhet, så det er et bedre sted å lagre disse definisjonene.

2.1. JPQL

Som standard bruker spørringsdefinisjonen JPQL.

La oss se på en enkel depotmetode som returnerer aktiv Bruker enheter fra databasen:

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

2.2. Innfødt

Vi kan også bruke innfødt SQL til å definere spørringen. Alt vi trenger å gjøre er angi verdien på nativeQuery tilskrive ekte og definere den opprinnelige SQL-spørringen i verdi merknadens attributt:

@Query (value = "SELECT * FRA BRUKERE u WHERE u.status = 1", nativeQuery = true) Collection findAllActiveUsersNative (); 

3. Definer ordre i en spørring

Vi kan sende en ekstra parameter av typen Sortere til en Spring Data-metodedeklarasjon som har @Spørsmål kommentar. Det blir oversatt til REKKEFØLGE ETTER klausul som blir sendt til databasen.

3.1. Sortering etter JPA-leverte og avledede metoder

For metodene vi får ut av boksen som findAll (Sorter) eller de som genereres ved signering av parsemetoder, vi kan bare bruke objektegenskaper til å definere vår sortering:

userRepository.findAll (ny sortering (Sort.Direction.ASC, "navn")); 

Tenk deg at vi vil sortere etter lengden på et navnegenskap:

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

Når vi utfører koden ovenfor, får vi et unntak:

org.springframework.data.mapping.PropertyReferenceException: Ingen egenskap LENGDE (navn) funnet for typen bruker!

3.2. JPQL

Når vi bruker JPQL for en spørredefinisjon, kan Spring Data håndtere sortering uten problemer - alt vi trenger å gjøre er å legge til en metodeparameter av typen Sortere:

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

Vi kan kalle denne metoden og sende en Sortere parameter, som vil bestille resultatet av Navn eiendommen til Bruker gjenstand:

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

Og fordi vi brukte @Spørsmål kommentar, kan vi bruke samme metode for å få den sorterte listen over Brukere etter lengden på navnene deres:

userRepository.findAllUsers (JpaSort.unsafe ("LENGDE (navn)")); 

Det er viktig at vi bruker JpaSort.unsafe () å lage en Sortere objektforekomst.

Når vi bruker:

ny sortering ("LENGDE (navn)"); 

så får vi nøyaktig samme unntak som vi så ovenfor for findAll () metode.

Når Spring Data oppdager det usikre Sortere bestille for en metode som bruker @Spørsmål kommentar, så legger den bare sorteringsklausulen til spørringen - den hopper over å sjekke om egenskapen som skal sorteres etter tilhører domenemodellen.

3.3. Innfødt

Når @Spørsmål kommentar bruker innfødt SQL, da er det ikke mulig å definere en Sortere.

Hvis vi gjør det, mottar vi et unntak:

org.springframework.data.jpa.repository.query.InvalidJpaQueryMethodException: Kan ikke bruke innfødte spørsmål med dynamisk sortering og / eller paginering

Som unntaket sier, støttes ikke sorteringen for innfødte spørsmål. Feilmeldingen gir oss et hint om at paginering også vil forårsake et unntak.

Imidlertid er det en løsning som muliggjør paginering, og vi vil dekke den i neste avsnitt.

4. Paginering

Paginering lar oss returnere bare en delmengde av et helt resultat i en Side. Dette er for eksempel nyttig når du navigerer gjennom flere sider med data på en webside.

En annen fordel med paginering er at datamengden som sendes fra server til klient minimeres. Ved å sende mindre data kan vi generelt se en forbedring i ytelsen.

4.1. JPQL

Å bruke paginering i JPQL-spørredefinisjonen er grei:

@Query (value = "SELECT u FROM User u ORDER BY id") Page findAllUsersWithPagination (Sidesidig side); 

Vi kan passere en PageRequest parameter for å få en side med data.

Paginering støttes også for innfødte spørsmål, men krever litt ekstra arbeid.

4.2. Innfødt

Vi kan aktivere paginering for innfødte spørsmål ved å erklære et tilleggsattributt countQuery.

Dette definerer SQL som skal utføres for å telle antall rader i hele resultatet:

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

4.3. Vårdata JPA-versjoner før 2.0.4

Ovennevnte løsning for innfødte spørsmål fungerer bra for Spring Data JPA versjoner 2.0.4 og nyere.

Før den versjonen, når vi prøver å utføre et slikt spørsmål, vil vi motta det samme unntaket som vi beskrev i forrige avsnitt om sortering.

Vi kan løse dette ved å legge til en ekstra parameter for paginering i spørringen:

@Query (value = "VELG * FRA BRUKERE BESTILL PÅ id \ n-- #pageable \ n", countQuery = "VELG antall (*) FRA brukere", nativeQuery = true) Side findAllUsersWithPagination (Pageable pageable);

I eksemplet ovenfor legger vi til “\ n– #pageable \ n” som plassholder for sideparameteren. Dette forteller Spring Data JPA hvordan man analyserer spørringen og injiserer den side som kan brukes. Denne løsningen fungerer for H2 database.

Vi har dekket hvordan du lager enkle utvalgte spørsmål via JPQL og native SQL. Deretter viser vi hvordan du definerer flere parametere.

5. Indekserte spørsmålsparametere

Det er to mulige måter vi kan overføre metodeparametere til spørringen vår: indekserte og navngitte parametere.

I denne delen vil vi dekke indekserte parametere.

5.1. JPQL

For indekserte parametere i JPQL vil Spring Data send metodeparametere til spørringen i samme rekkefølge som de vises i metodedeklarasjonen:

@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); 

For ovennevnte spørsmål, status metodeparameter tilordnes spørringsparameteren med indeks 1, og Navn metodeparameter vil bli tildelt spørringsparameteren med indeks 2.

5.2. Innfødt

Indekserte parametere for de innfødte spørringene fungerer nøyaktig på samme måte som for JPQL:

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

I neste avsnitt viser vi en annen tilnærming: overføring av parametere via navn.

6. Navngitte parametere

Vi kan også overfør metodeparametere til spørringen ved hjelp av navngitte parametere. Vi definerer disse ved hjelp av @Param kommentar i vår depotmetodedeklarasjon.

Hver parameter kommenteres med @Param må ha en verdistreng som samsvarer med det tilsvarende JPQL- eller SQL-spørringsparameternavnet. Et spørsmål med navngitte parametere er lettere å lese og er mindre utsatt for feil i tilfelle spørringen må omformuleres.

6.1. JPQL

Som nevnt ovenfor bruker vi @Param kommentar i metodedeklarasjonen for å matche parametere definert av navn i JPQL med parametere fra metodedeklarasjonen:

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

Merk at i eksemplet ovenfor definerte vi SQL-spørrings- og metodeparametrene til å ha de samme navnene, men det er ikke nødvendig så lenge verdistrengene er de samme:

@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. Innfødt

For den opprinnelige spørringsdefinisjonen er det ingen forskjell i hvordan vi sender en parameter via navnet til spørringen i forhold til JPQL - vi bruker @Param kommentar:

@Query (verdi = "VELG * FRA brukere u WHERE u.status =: status og u.name =: name", nativeQuery = true) User findUserByStatusAndNameNamedParamsNative (@Param ("status") Integer status, @Param ("name" ) Strengnavn);

7. Samlingsparameter

La oss vurdere saken når hvor klausul i JPQL- eller SQL-spørringen inneholder I (eller IKKE I) nøkkelord:

VELG u FRA bruker u HVOR u.navn IN: navn

I dette tilfellet kan vi definere en spørringsmetode som tar Samling som parameter:

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

Som parameteren er a Samling, den kan brukes med Liste, HashSet, etc.

Deretter viser vi hvordan du endrer data med @Endrer kommentar.

8. Oppdater spørringer med @Modifying

Vi kan bruke @Spørsmål kommentar for å endre tilstanden til databasen ved å legge til @Endrer kommentar til depotmetoden.

8.1. JPQL

Datalagermetoden som endrer dataene har to forskjeller i forhold til å velge spørring - den har @Modifying kommentar og selvfølgelig bruker JPQL-spørringen Oppdater i stedet for å velge:

@Modifying @Query ("oppdater bruker du setter u.status =: status der u.name =: name") int updateUserSetStatusForName (@Param ("status") Integer status, @Param ("name") Navn på streng); 

Returverdien definerer hvor mange rader kjøringen av spørringen ble oppdatert. Både indekserte og navngitte parametere kan brukes i oppdateringsspørsmål.

8.2. Innfødt

Vi kan endre tilstanden til databasen også med et eget søk. Vi trenger bare å legge til @Modifying kommentar:

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

8.3. Innlegg

For å utføre en innsatsoperasjon må vi begge søke @Modifying og bruk et innfødt spørsmål siden INSERT ikke er en del av JPA-grensesnittet:

@Modifying @Query (verdi = "sett inn i brukere (navn, alder, e-post, status) verdier (: navn,: alder,: e-post,: status)", nativeQuery = sann) ugyldig insertUser (@Param ("navn")) String name, @Param ("age") Integer age, @Param ("status") Integer status, @Param ("email") Streng email);

9. Dynamisk spørring

Ofte støter vi på behovet for å bygge SQL-setninger basert på forhold eller datasett hvis verdier bare er kjent under kjøretiden. Og i slike tilfeller kan vi ikke bare bruke et statisk spørsmål.

9.1. Eksempel på en dynamisk spørring

La oss for eksempel forestille oss en situasjon der vi trenger å velge alle brukerne som har e-post SOM en fra et sett definert ved kjøretid - e-post 1, e-post2, …, emailn:

VELG u FRA bruker u HVOR u.email LIKE '% email1%' eller u.email LIKE '% email2%' ... eller u.email LIKE '% emailn%'

Siden settet er dynamisk konstruert, kan vi ikke vite hvor mange det er på kompileringstidspunktet SOM klausuler å legge til.

I dette tilfellet, vi kan ikke bare bruke @Spørsmål kommentar siden vi ikke kan gi en statisk SQL-setning.

I stedet kan vi utvide basen ved å implementere et tilpasset sammensatt lager JpaRepository funksjonalitet og gi vår egen logikk for å bygge et dynamisk spørsmål. La oss ta en titt på hvordan du gjør dette.

9.2. Tilpassede arkiver og JPA Criteria API

Heldigvis for oss, Spring gir en måte å utvide baselageret ved bruk av tilpassede fragmentgrensesnitt. Vi kan deretter koble dem sammen for å lage et sammensatt lager.

Vi begynner med å lage et tilpasset fragmentgrensesnitt:

offentlig grensesnitt UserRepositoryCustom {List findUserByEmails (Set emails); }

Og så implementerer vi det:

offentlig klasse UserRepositoryCustomImpl implementerer UserRepositoryCustom {@PersistenceContext private EntityManager entityManager; @Override public List findUserByEmails (Set emails) {CriteriaBuilder cb = entityManager.getCriteriaBuilder (); CriteriaQuery-spørring = cb.createQuery (User.class); Rotbruker = query.from (User.class); Sti emailPath = user.get ("e-post"); Liste predikater = ny ArrayList (); for (String email: emails) {predicates.add (cb.like (emailPath, email)); } query.select (bruker) .where (cb.or (predicates.toArray (new Predicate [predicates.size ()]))); return entityManager.createQuery (spørring) .getResultList (); }}

Som vist ovenfor utnyttet vi JPA Criteria API for å bygge vårt dynamiske spørsmål.

Vi må også sørge for å inkludere Impl postfix i kursnavnet. Våren vil søke i UserRepositoryCustom implementering som UserRepositoryCustomImpl. Siden fragmenter ikke er repositorier av seg selv, er Spring avhengig av denne mekanismen for å finne implementeringen av fragmentet.

9.3. Utvide det eksisterende arkivet

Legg merke til at alle spørringsmetodene fra seksjon 2 til og med seksjon 7 er i UserRepository.

Så nå integrerer vi fragmentet vårt ved å utvide det nye grensesnittet i UserRepository:

offentlig grensesnitt UserRepository utvider JpaRepository, UserRepositoryCustom {// spørringsmetoder fra seksjon 2 - seksjon 7}

9.4. Bruke depotet

Og til slutt kan vi kalle vår dynamiske spørringsmetode:

Angi e-post = ny HashSet (); // fylle settet med et hvilket som helst antall elementer userRepository.findUserByEmails (emails); 

Vi har opprettet et sammensatt lager og kalt vår tilpassede metode.

10. Konklusjon

I denne artikkelen dekket vi flere måter å definere spørsmål i Spring Data JPA repository-metoder ved hjelp av @Spørsmål kommentar.

Vi lærte også hvordan du implementerer et tilpasset depot og lager et dynamisk spørsmål.

Som alltid er de komplette kodeeksemplene som brukes i denne artikkelen tilgjengelig på GitHub.


$config[zx-auto] not found$config[zx-overlay] not found