Spring Data Jpa @Query

Oversikt

Spring Data gir mange måter å definere en spørring som vi kan utføre. En av disse er @Query-merknaden.

i denne opplæringen viser vi hvordan du bruker @Query-merknaden I Spring Data JPA til å utføre BÅDE JPQL og native SQL-spørringer.

vi viser også hvordan du bygger en dynamisk spørring når @Query-merknaden ikke er nok.

Videre lesing:

Avledede Spørringsmetoder I Spring Data JPA Repositories

Utforsk spørringsavledningsmekanismen I Spring Data JPA.
Les mer →

Spring Data jpa @Modifying Annotation

Opprett dml-og DDL-spørringer I Spring Data JPA ved å kombinere @Query og @Modifying annotations
Les Mer →

Velg Query

for å definere SQL som skal utføres for En Fjærdatarepotemetode, kan vi annotere metoden med @Query-annotasjonen – verdiattributtet inneholder JPQL eller SQL som skal utføres —

@Query-merknaden har forrang over navngitte spørringer, som er merket med @NamedQuery eller definert i en orm.xml-fil.

Det er en god tilnærming til å plassere en spørringsdefinisjon like over metoden i depotet i stedet for i domenemodellen vår som navngitte spørringer. Depotet 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 aktive Brukerenheter fra databasen:

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

2.2. Native

Vi kan også bruke native SQL til å definere spørringen vår. Alt vi trenger å gjøre er å sette verdien av nativeQuery-attributtet til true og definere den innfødte SQL-spørringen i verdiattributtet til merknaden:

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

Definer Rekkefølge I En Spørring

Vi kan sende en ekstra parameter av typen Sortering Til En spring data method-deklarasjon som har @Query-merknaden. Det vil bli oversatt TIL ORDER BY-klausulen som blir sendt til databasen.

3.1. Sortering For Jpa Gitt Og Avledede Metoder

for metodene vi får ut av boksen som findAll(Sort) eller de som genereres ved parsing metode signaturer, kan vi bare bruke objektegenskaper til å definere vår sortering:

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

forestill Deg nå at vi vil sortere etter lengden på en navnegenskap:

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

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

org.springframework.data.kartlegging.PropertyReferenceException: ingen eiendom LENGDE (navn) funnet for typen Bruker!

3.2. JPQL

Når VI bruker JPQL for en spørringsdefinisjon, Kan Vårdata håndtere sortering uten problem.:

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

vi kan ringe denne metoden og sende En Sorteringsparameter, som vil bestille resultatet ved navnegenskapet Til Brukerobjektet:

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

Og fordi vi brukte @Query-merknaden, kan vi bruke samme metode for å få den sorterte Listen Over Brukere etter lengden på navnene sine:

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

Det er viktig at Vi bruker JpaSort.usikker () for å opprette En Sorter objektforekomst.

når vi bruker:

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

da får vi nøyaktig det samme unntaket som vi så over for findAll () – metoden.

Når Fjærdata oppdager den usikre Sorteringsrekkefølgen for en metode som bruker @ Query-merknaden, legger den bare til sorteringsklausulen i spørringen.

3.3. Opprinnelig

når @Query-merknaden bruker opprinnelig SQL, er det ikke mulig å definere En Sortering.

hvis vi gjør det, får vi et unntak:

org.springframework.data.jpa.depot.spørsmål.InvalidJpaQueryMethodException: kan ikke bruke opprinnelige spørringer med dynamisk sortering og/eller paginering

som unntaket sier, støttes ikke sorteringen for opprinnelige spørringer. Feilmeldingen gir oss et hint om at paginering vil føre til et unntak også.

det er imidlertid en løsning som muliggjør paginering, og vi dekker det i neste avsnitt.

Paginering

Paginering tillater oss å returnere bare et delsett av et helt resultat på En Side. Dette er nyttig, for eksempel når du navigerer gjennom flere sider med data på en nettside.

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

4.1. JPQL

Bruk av paginering i jpql-spørringsdefinisjonen er enkel:

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

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

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

4.2. Native

vi kan aktivere paginering for innfødte spørringer ved å erklære en ekstra attributt 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<User> findAllUsersWithPagination(Pageable pageable);

4.3. Spring Data JPA Versjoner Før 2.0.4

ovennevnte løsning for innfødte spørringer fungerer fint For Spring Data JPA versjoner 2.0.4 og senere.

Før den versjonen, når vi prøver å utføre en slik spørring, får vi det samme unntaket vi beskrev i forrige avsnitt om sortering.

Vi kan overvinne dette ved å legge til en ekstra parameter for paginering i spørringen vår:

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

i eksemplet ovenfor legger vi til «\n– #pageable\n » som plassholder for paginasjonsparameteren. Dette forteller Spring Data JPA hvordan å analysere spørringen og injisere pageable parameter. Denne løsningen fungerer For h2-databasen.

Vi har dekket hvordan du lager enkle utvalgsspørringer via JPQL og native SQL. Deretter viser vi hvordan du definerer flere parametere.

Indekserte Spørringsparametere

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

i denne delen dekker vi indekserte parametere.

5.1. JPQL

For indekserte parametere I JPQL, vil Fjærdata sende 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 de ovennevnte spørringene vil statusmetodeparameteren bli tilordnet spørringsparameteren med indeks 1, og navnemetodeparameteren vil bli tilordnet spørringsparameteren med indeks 2.

5.2. Innfødte

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

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

I neste avsnitt viser vi en annen tilnærming: passerer parametere via navn.

Navngitte Parametere

Vi kan også sende metodeparametere til spørringen ved hjelp av navngitte parametere. Vi definerer disse ved hjelp av @ Param annotasjonen i vår repository method declaration.

Hver parameter som er merket med @ Param, må ha en verdistreng som samsvarer med det tilsvarende jpql-eller SQL-spørringsparameternavnet. En spørring med navngitte parametere er enklere å lese og er mindre utsatt for feil i tilfelle spørringen må refactored.

6.1. JPQL

som nevnt ovenfor bruker vi @ Param-annotasjonen 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") String name);

Merk at i eksemplet ovenfor definerte VI SQL-spørringen og metodeparametrene for å 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. Native

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

@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. Samlingsparameter

la oss vurdere saken når where-klausulen i VÅR jpql – eller SQL-spørring inneholder in (or NOT IN) – søkeordet:

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

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<User> findUserByNameList(@Param("names") Collection<String> names);

som parameteren er En Samling, kan den brukes Med List, HashSet, etc.

Deretter viser Vi hvordan du endrer data med @Modifying-merknaden.

Oppdater Spørringer Med @Modifying

vi kan bruke @Query-merknaden til å endre tilstanden til databasen ved også å legge til @Modifying annotation til repository-metoden.

8.1. JPQL

depotmetoden som endrer dataene, har to forskjeller i forhold til utvalgsspørringen — :

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

returverdien definerer hvor mange rader kjøringen av spørringen oppdateres. Både indekserte og navngitte parametere kan brukes i oppdateringsspørringer.

8.2. Native

Vi kan endre tilstanden til databasen også med en innfødt spørring. Vi trenger bare å legge til @Modifying annotation:

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

8.3. Inserts

for å utføre en insert-operasjon må vi både bruke @Modifying og bruke en innfødt spørring siden INSERT ikke er en del AV jpa-grensesnittet:

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

Dynamisk Spørring

Ofte møter vi behovet for å bygge SQL-setninger basert på betingelser eller datasett hvis verdier bare er kjent under kjøring. Og i slike tilfeller kan vi ikke bare bruke en statisk spørring.

9.1. Eksempel På En Dynamisk Spørring

for eksempel, la oss forestille oss en situasjon der vi må velge alle brukerne hvis e-post ER som en fra et sett definert under kjøring-email1, email2,…, emailn:

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

Siden settet er dynamisk konstruert, kan vi ikke vite på kompileringstid hvor MANGE som klausuler å legge til.

i dette tilfellet kan vi ikke bare bruke @Query-merknaden siden vi ikke kan gi en statisk SQL-setning.

I Stedet, ved å implementere en tilpasset sammensatt depot, kan vi utvide basen jparepository funksjonalitet og gi vår egen logikk for å bygge en dynamisk spørring. La oss ta en titt på hvordan du gjør dette.

9.2. Custom Repositories OG Jpa Criteria API

Heldigvis for oss, Gir Våren en måte for å utvide basen depotet gjennom bruk av tilpassede fragment grensesnitt. Vi kan da koble dem sammen for å lage et sammensatt depot.

vi begynner med å lage et tilpasset fragmentgrensesnitt:

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

og så implementerer vi det:

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(); }}

som vist ovenfor, utnyttet VI JPA Criteria API for å bygge vår dynamiske spørring.

også, vi må sørge for å inkludere Impl postfix i klassenavnet. Våren vil søke I UserRepositoryCustom implementering Som UserRepositoryCustomImpl. Siden fragmenter ikke er repositorier av seg selv, er Våren avhengig av denne mekanismen for å finne fragmentimplementeringen.

9.3. Utvide Det Eksisterende Depotet

Legg Merke til at alle spørringsmetodene fra seksjon 2 til seksjon 7 er I UserRepository.

så, nå integrerer vi vårt fragment ved å utvide det nye grensesnittet I UserRepository:

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

9.4. Ved Hjelp Av Depotet

og til slutt kan vi ringe vår dynamiske spørringsmetode:

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

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

Konklusjon

i denne artikkelen dekket vi flere måter å definere spørringer I Spring Data jpa repository-metoder ved hjelp av @Query-merknaden.

vi lærte også å implementere et tilpasset depot og opprette en dynamisk spørring.

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

Legg igjen en kommentar

Din e-postadresse vil ikke bli publisert.