REST-forespørgselssprog med JPA-specifikationer for forårsdata

Denne artikel er en del af en serie: • REST Query Language with Spring and JPA Criteria

• REST-forespørgselssprog med Spring Data JPA-specifikationer (nuværende artikel) • REST-forespørgselssprog med Spring Data JPA og Querydsl

• REST-forespørgselssprog - Avanceret søgning

• REST Query Language - Implementering ELLER drift

• REST Query Language med RSQL

• REST forespørgselssprog med Querydsl Web Support

1. Oversigt

I denne vejledning - vi bygger en Søg / filtrer REST API ved hjælp af Spring Data JPA og specifikationer.

Vi begyndte at se på et forespørgselssprog i den første artikel i denne serie - med en JPA Criteria-baseret løsning.

Så - hvorfor et forespørgselssprog? Fordi - for enhver kompleks nok API - er søgning / filtrering af dine ressourcer efter meget enkle felter simpelthen ikke nok. Et forespørgselssprog er mere fleksibelt og giver dig mulighed for at filtrere ned til nøjagtigt de ressourcer, du har brug for.

2. Bruger Enhed

Først - lad os starte med et simpelt Bruger enhed til vores Search API:

@Entity offentlig klasse bruger {@Id @GeneratedValue (strategi = GenerationType.AUTO) privat Lang id; privat streng fornavn; privat streng efternavn; privat streng e-mail; privat int alder // standard getters og setter}

3. Filtrer ved hjælp Specifikation

Lad os nu komme direkte ind i den mest interessante del af problemet - forespørgsel med tilpasset Spring Data JPA specifikationer.

Vi opretter en Brugerspecifikation som implementerer Specifikation interface, og vi skal videregive i vores egen begrænsning til at konstruere den aktuelle forespørgsel:

offentlig klasse UserSpecification implementerer Specifikation {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 ()); } ellers hvis (criteria.getOperation (). er lig medIgnoreCase ("<")) {returner builder.lessThanOrEqualTo (root. get (criteria.getKey ()), criteria.getValue (). toString ()); } ellers hvis (criteria.getOperation (). er lig medIgnoreCase (":")) {if (root.get (criteria.getKey ()). getJavaType () == String.class) {return builder.like (root.get ( criteria.getKey ()), "%" + criteria.getValue () + "%"); } andet {return builder.equal (root.get (criteria.getKey ()), criteria.getValue ()); }} returner null; }}

Som vi kan se - vi skaber en Specifikation baseret på nogle enkle begrænsninger som vi repræsenterer i det følgende “Søgekriterier”Klasse:

offentlig klasse SearchCriteria {privat strengnøgle; privat streng operation; privat objektværdi }

Det Søgekriterier implementering har en grundlæggende repræsentation af en begrænsning - og den er baseret på denne begrænsning, som vi skal konstruere forespørgslen:

  • nøgle: feltnavnet - for eksempel fornavn, alder, … etc.
  • operation: operationen - for eksempel lighed, mindre end, ... osv.
  • værdi: feltværdien - for eksempel john, 25, ... osv.

Naturligvis er implementeringen forenklet og kan forbedres; det er dog en solid base for de kraftfulde og fleksible operationer, vi har brug for.

4. Den UserRepository

Næste - lad os tage et kig på UserRepository; vi udvider simpelthen JpaSpecificationExecutor for at få de nye specifikations-API'er:

offentlig grænseflade UserRepository udvider JpaRepository, JpaSpecificationExecutor {}

5. Test søgeforespørgsler

Lad os nu teste den nye API til søgning.

Lad os først oprette et par brugere for at have dem klar, når testene køres:

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

Lad os derefter se, hvordan man finder brugere med givet efternavn:

@Test offentligt ugyldigt givetLast_whenGettingListOfUsers_thenCorrect () {UserSpecification spec = new UserSpecification (new SearchCriteria ("lastName", ":", "doe")); Listeresultater = repository.findAll (spec); assertThat (userJohn, isIn (resultater)); assertThat (userTom, isIn (resultater)); }

Lad os nu se, hvordan man finder en bruger med givet både for- og efternavn:

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

Bemærk: Vi brugte “hvor”Og“og" til kombinere specifikationer.

Lad os derefter se, hvordan man finder en bruger med givet både efternavn og minimumsalder:

@Test offentlig ugyldighed givenLastAndAge_whenGettingListOfUsers_thenCorrect () {UserSpecification spec1 = new UserSpecification (new SearchCriteria ("age", ">", "25")); UserSpecification spec2 = ny UserSpecification (ny SearchCriteria ("efternavn", ":", "doe")); Listeresultater = repository.findAll (Specification.where (spec1) .and (spec2)); assertThat (userTom, isIn (resultater)); assertThat (brugerJohn, ikke (isIn (resultater))); }

Lad os nu se, hvordan man søger efter Bruger at eksisterer faktisk ikke:

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

Endelig - lad os se, hvordan man finder en Bruger kun givet en del af fornavnet:

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

6. Kombiner specifikationer

Dernæst - lad os se på at kombinere vores brugerdefinerede specifikationer at bruge flere begrænsninger og filtrere efter flere kriterier.

Vi skal implementere en bygherre - UserSpecificationsBuilder - til let og flydende at kombinere specifikationer:

offentlig klasse UserSpecificationsBuilder {private final Listeparametre; offentlig UserSpecificationsBuilder () {params = ny ArrayList (); } offentlig UserSpecificationsBuilder med (String key, String operation, Object value) {params.add (new SearchCriteria (key, operation, value)); returner dette; } public Specification build () {if (params.size () == 0) {return null; } Liste specs = params.stream () .map (UserSpecification :: new) .collect (Collectors.toList ()); Specifikationsresultat = 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

Endelig - lad os bruge denne nye persistenssøgning / filterfunktionalitet og opsæt REST API - ved at oprette en UserController med en simpel Søg operation:

@Controller offentlig klasse UserController {@Autowired privat UserRepository repo; @RequestMapping (metode = RequestMethod.GET, værdi = "/ brugere") @ResponseBody offentlig Listesøgning (@RequestParam (værdi = "søg") Strengsøgning) {UserSpecificationsBuilder builder = ny UserSpecificationsBuilder (); Mønster mønster = Mønster.kompil ("(\ w +?) (: |) (\ w +?),"); Matcher matcher = mønster.matcher (søg + ","); mens (matcher.find ()) {builder.with (matcher.group (1), matcher.group (2), matcher.group (3)); } Specifikationsspecifikation = builder.build (); returner repo.findAll (spec); }}

Bemærk, at for at understøtte andre ikke-engelske systemer, Mønster objekt kunne ændres som:

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

Her er et eksempel på en test-URL til test af API:

// localhost: 8080 / brugere? search = sidste navn: doe, alder> 25

Og svaret:

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

Da søgningerne er opdelt med et "," i vores Mønster F.eks. kan søgeudtrykkene ikke indeholde dette tegn. Mønsteret matcher heller ikke det hvide mellemrum.

Hvis vi ønsker at søge efter værdier, der indeholder kommaer, kan vi overveje at bruge en anden separator som “;”.

En anden mulighed ville være at ændre mønsteret for at søge efter værdier mellem anførselstegn og derefter fjerne disse fra søgeudtrykket:

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

8. Konklusion

Denne vejledning dækkede en simpel implementering, der kan være basen for et kraftigt REST-forespørgselssprog. Vi har gjort god brug af Spring Data Specifications for at sikre, at vi holder API væk fra domænet og har mulighed for at håndtere mange andre typer operationer.

Det fuld implementering af denne artikel kan findes i GitHub-projektet - dette er et Maven-baseret projekt, så det skal være let at importere og køre som det er.

Næste » REST Query Language med Spring Data JPA og Querydsl « Tidligere REST-forespørgselssprog med forårs- og JPA-kriterier

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