Spring Data JPA @forespørgsel

oversigt

Spring Data giver mange måder at definere en forespørgsel, som vi kan udføre. En af disse er @forespørgsel annotation.

i denne vejledning demonstrerer vi, hvordan du bruger @Forespørgselsanmærkningen i Spring Data JPA til at udføre både JPA og native JPA-forespørgsler.

vi viser også, hvordan man opbygger en dynamisk forespørgsel, når @Forespørgselsanmærkningen ikke er nok.

yderligere læsning:

afledte forespørgselsmetoder i Spring Data JPA Repositories

Udforsk forespørgselsafledningsmekanismen i Spring Data JPA.
Læs mere →

Spring Data JPA @modificerende Annotation

Opret DML og DDL forespørgsler i Spring Data JPA ved at kombinere @forespørgsel og @modificerende anmærkninger
Læs mere →

Vælg forespørgsel

for at definere KVKL, der skal udføres for en fjeder — datalagringsmetode, kan vi kommentere metoden med @Forespørgselsannotationen-dens værdiattribut indeholder JPKL eller KVKL, der skal udføres.

@Forespørgselsanmærkningen har forrang frem for navngivne forespørgsler, som er kommenteret med @Namedforespørgsel eller defineret i en orm.fil.

det er en god tilgang til at placere en forespørgselsdefinition lige over metoden inde i lageret snarere end inde i vores domænemodel som navngivne forespørgsler. Depotet er ansvarlig for vedholdenhed, så det er et bedre sted at gemme disse definitioner.

2.1.

som standard bruger forespørgselsdefinitionen JPKL.

lad os se på en simpel lagermetode, der returnerer aktive brugerenheder fra databasen:

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

2.2. Native

vi kan også bruge native kvm til at definere vores forespørgsel. Alt, hvad vi skal gøre, er at indstille værdien af attributten nativforespørgsel til sand og definere den native KVL-forespørgsel i værdiattributten for annotationen:

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

Definer rækkefølge i en forespørgsel

vi kan overføre en ekstra parameter af typesortering til en Fjederdatametodedeklaration, der har @Forespørgselsanmærkningen. Det vil blive oversat til den rækkefølge efter klausul, der bliver sendt til databasen.

3.1. Sortering for JPA leverede og afledte metoder

for de metoder, vi kommer ud af boksen, såsom findAll(Sorter) eller dem, der genereres ved parsing af metodesignaturer, kan vi kun bruge objektegenskaber til at definere vores sortering:

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

forestil dig nu, at vi vil sortere efter længden af en navneegenskab:

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

når vi udfører ovenstående kode, modtager vi en undtagelse:

org.springramme.data.kortlægning.Propertyreferenceundtagelse: ingen ejendom længde (navn) fundet for type bruger!

3.2.

når vi bruger JPKL til en forespørgselsdefinition, kan Springdata håndtere sortering uden problemer – alt hvad vi skal gøre er at tilføje en metodeparameter af type sortering:

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

vi kan kalde denne metode og videregive en Sorteringsparameter, som vil bestille resultatet ved navneegenskaben for brugerobjektet:

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

og fordi vi brugte @Forespørgselsanmærkningen, kan vi bruge den samme metode til at få den sorterede liste over brugere efter længden af deres navne:

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

det er vigtigt, at vi bruger JpaSort.usikker () for at oprette en sorteringsobjektinstans.

når vi bruger:

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

så får vi nøjagtig den samme undtagelse som vi så ovenfor for findAll () – metoden.

når Spring Data opdager den usikre sorteringsrækkefølge for en metode, der bruger @Forespørgselsnotationen, tilføjer den bare sorteringsklausulen til forespørgslen — den springer over at kontrollere, om egenskaben, der skal sorteres efter, tilhører domænemodellen.

3.3. Native

når @Forespørgselsanmærkningen bruger native kvm, er det ikke muligt at definere en sortering.

hvis vi gør det, modtager vi en undtagelse:

org.springramme.data.jpa.depot.forespørgsel.Kan ikke bruge oprindelige forespørgsler med dynamisk sortering og/eller paginering

som undtagelsen siger, understøttes sorteringen ikke for oprindelige forespørgsler. Fejlmeddelelsen giver os et tip om, at paginering også vil medføre en undtagelse.

der er dog en løsning, der muliggør paginering, og vi dækker det i næste afsnit.

paginering

paginering giver os mulighed for at returnere kun en delmængde af et helt resultat på en side. Dette er nyttigt, for eksempel, når du navigerer gennem flere sider med data på en hjemmeside.

en anden fordel ved paginering er, at mængden af data, der sendes fra server til klient, minimeres. Ved at sende mindre stykker data kan vi generelt se en forbedring af ydeevnen.

4.1.

brug af paginering i JPKL-forespørgselsdefinitionen er ligetil:

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

vi kan passere en sideanmodningsparameter for at få en side med data.

paginering understøttes også for indfødte forespørgsler, men kræver lidt ekstra arbejde.

4.2. Native

vi kan aktivere paginering for native forespørgsler ved at erklære en ekstra attribut countforespørgsel.

dette definerer den KVL, der skal udføres for at tælle antallet af rækker 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 versioner før 2.0.4

ovenstående løsning til native forespørgsler fungerer fint for Spring Data JPA versioner 2.0.4 og nyere.

før den version, når vi forsøger at udføre en sådan forespørgsel, modtager vi den samme undtagelse, som vi beskrev i det foregående afsnit om sortering.

vi kan overvinde dette ved at tilføje en ekstra parameter til paginering inde i vores forespørgsel:

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

i ovenstående eksempel tilføjer vi “\n– #pageable\n” som pladsholder for pagineringsparameteren. Dette fortæller Spring Data JPA, hvordan man analyserer forespørgslen og injicerer den pageable parameter. Denne løsning fungerer til H2-databasen.

vi har dækket, hvordan du opretter enkle udvælgelsesforespørgsler via . Dernæst viser vi, hvordan du definerer yderligere parametre.

indekserede forespørgselsparametre

der er to mulige måder, hvorpå vi kan videregive metodeparametre til vores forespørgsel: indekserede og navngivne parametre.

i dette afsnit dækker vi indekserede parametre.

5.1.

for indekserede parametre i JPKL vil Fjederdata overføre metodeparametre til forespørgslen i samme rækkefølge, som de vises i metodedeklarationen:

@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 ovenstående forespørgsler tildeles statusmetodeparameteren til forespørgselsparameteren med indeks 1, og navnemetodeparameteren tildeles forespørgselsparameteren med indeks 2.

5.2. Native

indekserede parametre for de native forespørgsler fungerer nøjagtigt på samme måde som for:

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

i det næste afsnit viser vi en anden tilgang: passerer parametre via navn.

navngivne parametre

vi kan også videregive metodeparametre til forespørgslen ved hjælp af navngivne parametre. Vi definerer disse ved hjælp af @Param annotation inde i vores repository metode erklæring.

hver parameter, der er kommenteret med @Param, skal have en værdistreng, der matcher det tilsvarende navn på forespørgselsparameteren. En forespørgsel med navngivne parametre er lettere at læse og er mindre fejlbehæftet, hvis forespørgslen skal refactoreres.

6.1.

som nævnt ovenfor bruger vi @Param-annotationen i metodedeklarationen til at matche parametre defineret ved navn i JPKL med parametre fra metodedeklarationen:

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

Bemærk, at vi i ovenstående eksempel definerede vores forespørgsels-og metodeparametre for at have de samme navne, men det kræves ikke, så længe værdistrengene 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 oprindelige forespørgselsdefinition er der ingen forskel i, hvordan vi overfører en parameter via navnet til forespørgslen i forhold til JPKL-vi bruger @Param-annotationen:

@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

lad os overveje sagen, når hvor-klausulen i vores forespørgsel indeholder nøgleordet i (eller ikke i) :

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

i dette tilfælde kan vi definere en forespørgselsmetode, der tager samling som en parameter:

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

da parameteren er en samling, kan den bruges med liste, HashSet osv.

dernæst viser vi, hvordan du ændrer data med @modificerende annotation.

Opdater forespørgsler med @modificerende

vi kan bruge @Forespørgselsanmærkningen til at ændre databasens tilstand ved også at tilføje @modificerende annotation til lagermetoden.

8.1. 5128>

lagringsmetoden, der ændrer dataene, har to forskelle i forhold til udvælgelsesforespørgslen — den har @modificerende annotation, og selvfølgelig bruger JPKL-forespørgslen opdatering i stedet for at vælge:

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

returværdien definerer, hvor mange rækker udførelsen af forespørgslen opdateres. Både indekserede og navngivne parametre kan bruges i opdateringsforespørgsler.

8.2. Native

vi kan ændre databasens tilstand også med en oprindelig forespørgsel. Vi skal bare tilføje @modificerende annotation:

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

8.3. Indsætter

for at udføre en indsatshandling skal vi både anvende @ændring og bruge en oprindelig forespørgsel, da Indsæt ikke er en del af JPA-grænsefladen:

@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 forespørgsel

ofte vil vi støde på behovet for at opbygge kvm-udsagn baseret på betingelser eller datasæt, hvis værdier kun er kendt under kørsel. Og i disse tilfælde kan vi ikke bare bruge en statisk forespørgsel.

9.1. Eksempel på en dynamisk forespørgsel

lad os for eksempel forestille os en situation, hvor vi skal vælge alle de brugere, hvis e-mail er som en fra et sæt defineret ved runtime-email1, email2,…, emailn:

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

da sættet er dynamisk konstrueret, kan vi ikke vide på kompileringstidspunktet, hvor mange lignende klausuler der skal tilføjes.

i dette tilfælde kan vi ikke bare bruge @Forespørgselsanmærkningen, da vi ikke kan give en statisk KVL-erklæring.

i stedet kan vi ved at implementere et brugerdefineret kompositlager udvide base JpaRepository-funktionaliteten og give vores egen logik til opbygning af en dynamisk forespørgsel. Lad os se på, hvordan du gør dette.

9.2. Custom Repositories og JPA Criteria API

heldigvis for os, Spring giver en måde for at udvide basen repository gennem brug af brugerdefinerede fragment grænseflader. Vi kan derefter forbinde dem sammen for at oprette et sammensat lager.

vi starter med at oprette en brugerdefineret fragmentgrænseflade:

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 udnyttede vi JPA-kriterierne API til at opbygge vores dynamiske forespørgsel.

vi skal også sørge for at medtage Impl-postrettelsen i klassenavnet. Foråret vil søge userrepositorycustom implementering som UserRepositoryCustomImpl. Da fragmenter ikke er opbevaringssteder i sig selv, er Spring afhængig af denne mekanisme for at finde fragmentimplementeringen.

9.3. Udvidelse af det eksisterende lager

Bemærk, at alle forespørgselsmetoder fra afsnit 2 til afsnit 7 er i UserRepository.

så nu integrerer vi vores fragment ved at udvide den nye grænseflade i UserRepository:

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

9.4. Ved hjælp af depotet

og endelig kan vi kalde vores dynamiske forespørgselsmetode:

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

vi har med succes oprettet et sammensat lager og kaldt vores brugerdefinerede metode.

konklusion

i denne artikel dækkede vi flere måder at definere forespørgsler i Spring Data JPA repository metoder ved hjælp af @forespørgsel annotation.

vi lærte også, hvordan man implementerer et brugerdefineret lager og opretter en dynamisk forespørgsel.

som altid er de komplette kodeeksempler, der bruges i denne artikel, tilgængelige på GitHub.

Skriv et svar

Din e-mailadresse vil ikke blive publiceret.