REST Query Language med vårdata JPA-spesifikasjoner

Denne artikkelen er en del av en serie: • REST Query Language with Spring and JPA Criteria

• REST Query Language with Spring Data JPA Specifications (nåværende artikkel) • REST Query Language with Spring Data JPA and Querydsl

• REST-spørrespråk - Avanserte søkeoperasjoner

• REST Query Language - Implementering ELLER drift

• REST Query Language med RSQL

• REST Query Language med Querydsl Web Support

1. Oversikt

I denne opplæringen - vil vi bygge en Søk / filtrer REST API ved hjelp av Spring Data JPA og spesifikasjoner.

Vi begynte å se på et spørrespråk i den første artikkelen i denne serien - med en JPA Criteria-basert løsning.

Så - hvorfor et spørrespråk? Fordi - for ethvert komplekst nok API - er det ikke nok å søke / filtrere ressursene dine etter veldig enkle felt. Et spørrespråk er mer fleksibelt og lar deg filtrere ned til nøyaktig de ressursene du trenger.

2. Bruker Enhet

Først - la oss starte med en enkel Bruker enhet for vårt søk-API:

@Entity offentlig klasse bruker {@Id @GeneratedValue (strategi = GenerationType.AUTO) privat Lang id; privat streng fornavn; privat streng etternavn; privat streng e-post; privat alder; // standard getters og setters}

3. Filtrer ved hjelp Spesifikasjon

Nå - la oss komme rett inn i den mest interessante delen av problemet - spørring med tilpasset Spring Data JPA Spesifikasjoner.

Vi lager en Brukerspesifikasjon som implementerer Spesifikasjon grensesnitt og vi skal pass i vår egen begrensning for å konstruere selve spørringen:

offentlig klasse UserSpecification implementerer spesifikasjon {private SearchCriteria kriterier; @Override public Predicate toPredicate (Root root, CriteriaQuery query, CriteriaBuilder builder) {if (criteria.getOperation (). EqualsIgnoreCase (">")) {return builder.greaterThanOrEqualTo (root. Get (criteria.getKey ()), kriterier. getValue (). toString ()); } annet hvis (criteria.getOperation (). equalsIgnoreCase ("<")) {return builder.lessThanOrEqualTo (root. get (criteria.getKey ()), criteria.getValue (). toString ()); } annet hvis (criteria.getOperation (). equalsIgnoreCase (":")) {if (root.get (criteria.getKey ()). getJavaType () == String.class) {return builder.like (root.get ( criteria.getKey ()), "%" + criteria.getValue () + "%"); } annet {return builder.equal (root.get (criteria.getKey ()), criteria.getValue ()); }} returner null; }}

Som vi kan se - vi lager en Spesifikasjon basert på noen enkle begrensninger som vi representerer i det følgende “Søkekriterier”Klasse:

offentlig klasse SearchCriteria {privat strengnøkkel; privat streng operasjon; privat Objektverdi; }

De Søkekriterier implementering har en grunnleggende representasjon av en begrensning - og den er basert på denne begrensningen at vi skal konstruere spørringen:

  • nøkkel: feltnavnet - for eksempel fornavn, alder, … etc.
  • operasjon: operasjonen - for eksempel likestilling, mindre enn, ... etc.
  • verdi: feltverdien - for eksempel john, 25, ... etc.

Selvfølgelig er implementeringen forenklet og kan forbedres; det er imidlertid en solid base for de kraftige og fleksible operasjonene vi trenger.

4. Den UserRepository

Neste - la oss ta en titt på UserRepository; vi utvider ganske enkelt JpaSpecificationExecutor for å få de nye spesifikasjons-APIene:

offentlig grensesnitt UserRepository utvider JpaRepository, JpaSpecificationExecutor {}

5. Test søkene

Nå - la oss teste ut det nye søke-API-et.

Først, la oss lage noen brukere for å ha dem klare når testene kjører:

@RunWith (SpringJUnit4ClassRunner.class) @ContextConfiguration (classes = {PersistenceJPAConfig.class}) @ Transactional @TransactionConfiguration public class JPASpecificationsTest {@Autowired private UserRepository repository; privat bruker brukerJohn; privat bruker userTom; @Før offentlig ugyldig init () {userJohn = ny bruker (); userJohn.setFirstName ("John"); userJohn.setLastName ("Doe"); userJohn.setEmail ("[email protected]"); userJohn.setAge (22); repository.save (userJohn); userTom = ny bruker (); userTom.setFirstName ("Tom"); userTom.setLastName ("Doe"); userTom.setEmail ("[email protected]"); userTom.setAge (26); repository.save (userTom); }}

La oss deretter se hvordan du finner brukere med gitt etternavn:

@Test offentlig ugyldig gittLast_whenGettingListOfUsers_thenCorrect () {UserSpecification spec = new UserSpecification (new SearchCriteria ("lastName", ":", "doe")); Listeresultater = repository.findAll (spesifikasjon); assertThat (userJohn, isIn (resultater)); assertThat (userTom, isIn (resultater)); }

La oss nå se hvordan du finner en bruker med gitt både for- og etternavn:

@Test offentlig ugyldig givenFirstAndLastName_whenGettingListOfUsers_thenCorrect () {UserSpecification spec1 = new UserSpecification (new SearchCriteria ("firstName", ":", "john")); UserSpecification spec2 = ny UserSpecification (ny SearchCriteria ("etternavn", ":", "doe")); Listeresultater = repository.findAll (Specification.where (spec1) .and (spec2)); assertThat (userJohn, isIn (resultater)); assertThat (userTom, not (isIn (results))); }

Merk: Vi brukte “hvor”Og”og" til kombinere spesifikasjoner.

Deretter, la oss se hvordan du finner en bruker med gitt både etternavn og aldersgrense:

@Test offentlig ugyldig gittLastAndAge_whenGettingListOfUsers_thenCorrect () {UserSpecification spec1 = new UserSpecification (new SearchCriteria ("age", ">", "25")); UserSpecification spec2 = ny UserSpecification (ny SearchCriteria ("etternavn", ":", "doe")); Listeresultater = repository.findAll (Specification.where (spec1) .and (spec2)); assertThat (userTom, isIn (resultater)); assertThat (userJohn, not (isIn (results))); }

La oss nå se hvordan vi kan søke etter Bruker at eksisterer faktisk ikke:

@Test offentlig ugyldig givenWrongFirstAndLast_whenGettingListOfUsers_thenCorrect () {UserSpecification spec1 = new UserSpecification (new SearchCriteria ("firstName", ":", "Adam")); UserSpecification spec2 = ny UserSpecification (ny SearchCriteria ("etternavn", ":", "Fox")); Listeresultater = repository.findAll (Specification.where (spec1) .and (spec2)); assertThat (userJohn, not (isIn (results))); assertThat (userTom, not (isIn (results))); }

Til slutt - la oss se hvordan du finner en Bruker bare gitt en del av fornavnet:

@Test offentlig ugyldig givenPartialFirst_whenGettingListOfUsers_thenCorrect () {UserSpecification spec = new UserSpecification (new SearchCriteria ("firstName", ":", "jo")); Listeresultater = repository.findAll (spesifikasjon); assertThat (userJohn, isIn (resultater)); assertThat (userTom, not (isIn (results))); }

6. Kombiner Spesifikasjoner

Neste - la oss ta en titt på å kombinere vår skikk Spesifikasjoner å bruke flere begrensninger og filtrere i henhold til flere kriterier.

Vi skal implementere en byggherre - UserSpecificationsBuilder - å kombinere enkelt og flytende Spesifikasjoner:

offentlig klasse UserSpecificationsBuilder {private final List params; public UserSpecificationsBuilder () {params = new ArrayList (); } offentlig UserSpecificationsBuilder med (strengnøkkel, strengoperasjon, objektverdi) {params.add (ny SearchCriteria (nøkkel, operasjon, verdi)); returner dette; } public Specification build () {if (params.size () == 0) {return null; } Liste spesifikasjoner = params.stream () .map (UserSpecification :: new) .collect (Collectors.toList ()); Spesifikasjonsresultat = specs.get (0); for (int i = 1; i <params.size (); i ++) {result = params.get (i) .isOrPredicate ()? Specification.where (result) .or (specs.get (i)): Specification.where (result) .and (specs.get (i)); } returnere resultat; }}

7. UserController

Til slutt - la oss bruke denne nye funksjonaliteten for persistenssøk / filter og sette opp REST API - ved å lage en UserController med en enkel Søk operasjon:

@Controller offentlig klasse UserController {@Autowired privat UserRepository repo; @RequestMapping (method = RequestMethod.GET, value = "/ users") @ResponseBody public List search (@RequestParam (value = "search") String search) {UserSpecificationsBuilder builder = new UserSpecificationsBuilder (); Mønstermønster = Mønster.kompilere ("(\ w +?) (: |) (\ w +?),"); Matcher matcher = mønster.matcher (søk + ","); mens (matcher.find ()) {builder.with (matcher.group (1), matcher.group (2), matcher.group (3)); } Spesifikasjonsspesifikasjon = builder.build (); returner repo.findAll (spesifikasjon); }}

Merk at for å støtte andre ikke-engelske systemer, Mønster objektet kunne endres som:

Mønstermønster = Mønster.kompilere ("(\ w +?) (: |) (\ w +?),", Mønster.UNICODE_CHARACTER_CLASS);

Her er et eksempel på en test-URL for å teste ut API:

// localhost: 8080 / brukere? search = etternavn: doe, alder> 25

Og svaret:

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

Siden søkene er delt med et “,” i vår Mønster Eksempel: søkeord kan ikke inneholde dette tegnet. Mønsteret samsvarer heller ikke med mellomrom.

Hvis vi vil søke etter verdier som inneholder komma, kan vi vurdere å bruke en annen skilletegn som ";".

Et annet alternativ vil være å endre mønsteret for å søke etter verdier mellom anførselstegn, og deretter fjerne disse fra søkeordet:

Mønstermønster = Mønster.kompil ("(\ w +?) (: |) (\" ([^ \ "] +) \") ");

8. Konklusjon

Denne opplæringen dekket en enkel implementering som kan være basen i et kraftig REST-spørringsspråk. Vi har gjort god bruk av Spring Data Specifications for å sikre at vi holder APIen borte fra domenet og har muligheten til å håndtere mange andre typer operasjoner.

De full gjennomføring av denne artikkelen finnes i GitHub-prosjektet - dette er et Maven-basert prosjekt, så det skal være enkelt å importere og kjøre som det er.

Neste » REST Query Language med vårdata JPA og Querydsl « Forrige REST-spørrespråk med vår- og JPA-kriterier

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