REST jezik upita s Spring Data JPA specifikacijama

Ovaj je članak dio serije: • REST jezik upita s proljetnim i JPA kriterijima

• REST jezik upita s Spring Data JPA specifikacijama (trenutni članak) • REST jezik upita s Spring podacima JPA i Querydsl

• REST jezik upita - Napredno pretraživanje

• REST upitni jezik - implementacija ILI operacija

• REST jezik upita s RSQL-om

• OSTALI jezik upita s web podrškom Querydsl

1. Pregled

U ovom vodiču - izradit ćemo a API za pretraživanje / filtriranje REST koristeći Spring Data JPA i specifikacije.

Jezik upita počeli smo razmatrati u prvom članku ove serije - s rješenjem temeljenim na kriterijima JPA.

Dakle - zašto jezik upita? Jer - za bilo koji dovoljno složen API - pretraživanje / filtriranje vaših resursa po vrlo jednostavnim poljima jednostavno nije dovoljno. Jezik upita fleksibilniji je i omogućuje vam filtriranje do točno potrebnih resursa.

2. Korisnik Entitet

Prvo - krenimo s jednostavnim Korisnik entitet za naš API pretraživanja:

@Entity javna klasa Korisnik {@Id @GeneratedValue (strategy = GenerationType.AUTO) private Long id; private String firstName; private String lastName; privatni String e-mail; privatno int doba; // standardni geteri i postavljači}

3. Filtriranje pomoću Specifikacija

Sada - krenimo odmah u najzanimljiviji dio problema - postavljanje upita s prilagođenim Spring Data JPA Tehnički podaci.

Stvorit ćemo a Specifikacija korisnika koja provodi Specifikacija sučelje i idemo predati vlastito ograničenje za konstrukciju stvarnog upita:

javna klasa UserSpecification provodi specifikaciju {privatni kriteriji pretraživanja; @Override public Predicate toPredicate (root root, CriteriaQuery query, CriteriaBuilder builder) {if (kriterij.getOperation (). EqualsIgnoreCase (">")) {return builder.greaterThanOrEqualTo (root. Get (kriterij.getKey ()), kriteriji. getValue (). toString ()); } inače if (kriterij.getOperation (). equalsIgnoreCase ("<")) {return builder.lessThanOrEqualTo (root. get (kriterij.getKey ()), criteria.getValue (). toString ()); } else if (criteria.getOperation (). equalsIgnoreCase (":")) {if (root.get (kriterij.getKey ()). getJavaType () == String.class) {return builder.like (root.get ( kriterij.getKey ()), "%" + kriterij.getValue () + "%"); } else {vratiti graditelj.equal (root.get (kriterij.getKey ()), kriterij.getValue ()); }} return null; }}

Kao što vidimo - mi stvaramo a Specifikacija na temelju nekih jednostavnih ograničenja koje predstavljamo u sljedećem “Kriterij pretrage”Klasa:

public class SearchCriteria {private String key; operacija privatnog niza; vrijednost privatnog objekta; }

The Kriterij pretrage implementacija sadrži osnovni prikaz ograničenja - i na temelju tog ograničenja ćemo konstruirati upit:

  • ključ: naziv polja - na primjer, ime, dob, ... itd.
  • operacija: operacija - na primjer, jednakost, manja od, ... itd.
  • vrijednost: vrijednost polja - na primjer, john, 25, ... itd.

Naravno, provedba je pojednostavljena i može se poboljšati; to je međutim čvrsta osnova za snažne i fleksibilne operacije koje su nam potrebne.

4. The UserRepository

Dalje - pogledajmo UserRepository; mi jednostavno produžujemo JpaSpecificationExeecuter da biste dobili nove API-je specifikacija:

javno sučelje UserRepository proširuje JpaRepository, JpaSpecificationExecutor {}

5. Testirajte upite za pretraživanje

Sada - isprobajmo novi API za pretraživanje.

Prvo stvorimo nekoliko korisnika da budu spremni za vrijeme testiranja:

@RunWith (SpringJUnit4ClassRunner.class) @ContextConfiguration (classes = {PersistenceJPAConfig.class}) @Transactional @TransactionConfiguration javna klasa JPASpecificationsTest {@Autowired private UserRepository repository; privatni korisnik userJohn; privatni korisnik userTom; @Prije javne void init () {userJohn = novi korisnik (); userJohn.setFirstName ("Ivan"); userJohn.setLastName ("Doe"); userJohn.setEmail ("[e-pošta zaštićena]"); userJohn.setAge (22); repository.save (userJohn); userTom = novi korisnik (); userTom.setFirstName ("Tom"); userTom.setLastName ("Doe"); userTom.setEmail ("[e-pošta zaštićena]"); userTom.setAge (26); repository.save (userTom); }}

Dalje, pogledajmo kako pronaći korisnike s dato prezime:

@Test public void givenLast_whenGettingListOfUsers_thenCorrect () {UserSpecification spec = new UserSpecification (new SearchCriteria ("lastName", ":", "doe")); Popis rezultata = repozitorij.findAll (spec.); assertThat (userJohn, isIn (rezultati)); assertThat (userTom, isIn (rezultati)); }

Sada, da vidimo kako pronaći korisnika s danim i ime i prezime:

@Test public void givenFirstAndLastName_whenGettingListOfUsers_thenCorrect () {UserSpecification spec1 = new UserSpecification (new SearchCriteria ("firstName", ":", "john")); UserSpecification spec2 = nova UserSpecification (novi kriteriji pretraživanja ("lastName", ":", "doe")); Popis rezultata = spremište.findAll (Specification.where (spec1) .a (spec2)); assertThat (userJohn, isIn (rezultati)); assertThat (userTom, ne (isIn (rezultati))); }

Napomena: Koristili smo “gdje"I"i”Do kombinirati Specifikacije.

Dalje, da vidimo kako pronaći korisnika s danim i prezime i minimalna dob:

@Test javna praznina givenLastAndAge_whenGettingListOfUsers_thenCorrect () {UserSpecification spec1 = new UserSpecification (novi kriteriji pretraživanja ("age", ">", "25")); UserSpecification spec2 = nova UserSpecification (novi kriteriji pretraživanja ("lastName", ":", "doe")); Popis rezultata = spremište.findAll (Specification.where (spec1) .a (spec2)); assertThat (userTom, isIn (rezultati)); assertThat (userJohn, ne (isIn (rezultati))); }

Sada, da vidimo kako tražiti Korisnik da zapravo ne postoji:

@Test javna praznina givenWrongFirstAndLast_whenGettingListOfUsers_thenCorrect () {UserSpecification spec1 = new UserSpecification (novi kriteriji pretraživanja ("firstName", ":", "Adam")); UserSpecification spec2 = nova UserSpecification (novi kriteriji pretraživanja ("lastName", ":", "Fox")); Popis rezultata = spremište.findAll (Specification.where (spec1) .a (spec2)); assertThat (userJohn, ne (isIn (rezultati))); assertThat (userTom, ne (isIn (rezultati))); }

Napokon - da vidimo kako pronaći a Korisnik dobio samo dio imena:

@Test javna praznina givenPartialFirst_whenGettingListOfUsers_thenCorrect () {UserSpecification spec = new UserSpecification (novi kriteriji pretraživanja ("firstName", ":", "jo")); Popis rezultata = repozitorij.findAll (spec.); assertThat (userJohn, isIn (rezultati)); assertThat (userTom, ne (isIn (rezultati))); }

6. Kombinirajte Tehnički podaci

Dalje - pogledajmo kombiniranje naših običaja Tehnički podaci koristiti više ograničenja i filtrirati prema više kriterija.

Primijenit ćemo graditelj - UserSpecificationsBuilder - za jednostavno i tečno kombiniranje Tehnički podaci:

javna klasa UserSpecificationsBuilder {privatni konačni parametri popisa; javni UserSpecificationsBuilder () {params = novi ArrayList (); } javni UserSpecificationsBuilder sa (ključ niza, operacija niza, vrijednost objekta) {params.add (novi kriteriji pretraživanja (ključ, operacija, vrijednost)); vrati ovo; } javna specifikacija build () {if (params.size () == 0) {return null; } Popis specifikacija = params.stream () .map (UserSpecification :: new) .collect (Collectors.toList ()); Rezultat specifikacije = specs.get (0); za (int i = 1; i <params.size (); i ++) {rezultat = params.get (i) .isOrPredicate ()? Specification.where (rezultat) .ili (specs.get (i)): Specification.where (rezultat) .i (specs.get (i)); } vratiti rezultat; }}

7. UserController

Napokon - iskoristimo ovu novu funkciju trajanja / filtracije trajnosti i postaviti REST API - stvaranjem a UserController s jednostavnim traži operacija:

@Controller javna klasa UserController {@Autowired private UserRepository repo; @RequestMapping (method = RequestMethod.GET, value = "/ users") @ResponseBody pretraživanje javnog popisa (@RequestParam (value = "search") Pretraživanje niza) {UserSpecificationsBuilder builder = new UserSpecificationsBuilder (); Uzorak uzorka = Pattern.compile ("(\ w +?) (: |) (\ w +?),"); Podudaranje podudaranja = pattern.matcher (pretraživanje + ","); while (matcher.find ()) {builder.with (matcher.group (1), matcher.group (2), matcher.group (3)); } Specifikacija spec = builder.build (); vratiti repo.findAll (spec); }}

Imajte na umu da za podršku drugim sustavima koji nisu engleski, Uzorak objekt se može promijeniti kao:

Uzorak uzorka = Pattern.compile ("(\ w +?) (: |) (\ w +?),", Uzorak.UNICODE_CHARACTER_CLASS);

Evo primjera testnog URL-a za testiranje API-ja:

// localhost: 8080 / users? search = lastName: doe, dob> 25

I odgovor:

[{"id": 2, "firstName": "tom", "lastName": "doe", "email": "[email protected]", "age": 26}]

Budući da su pretrage podijeljene s "," u našem Uzorak na primjer, pojmovi za pretraživanje ne mogu sadržavati ovaj znak. Uzorak se također ne podudara s razmakom.

Ako želimo tražiti vrijednosti koje sadrže zareze, onda možemo razmotriti upotrebu drugog separatora poput ";".

Druga opcija bila bi promjena uzorka za traženje vrijednosti između navodnika, a zatim uklanjanje ovih iz pojma za pretraživanje:

Uzorak uzorka = Uzorak.compile ("(\ w +?) (: |) (\" ([^ \ "] +) \") ");

8. Zaključak

Ovaj je vodič obuhvatio jednostavnu implementaciju koja može biti osnova moćnog jezika upita REST. Dobro smo iskoristili Spring Data Specifications kako bismo bili sigurni da API držimo podalje od domene i imaju mogućnost rukovanja mnogim drugim vrstama operacija.

The puna provedba ovog članka može se naći u projektu GitHub - ovo je projekt zasnovan na Mavenu, pa bi ga trebalo lako uvesti i pokrenuti kakav jest.

Sljedeći » REST jezik upita s Spring Data JPA i Querydsl « Prethodni REST jezik upita s proljetnim i JPA kriterijima

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