REST Query Language med Spring Data JPA og Querydsl

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

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

• REST Query Language med Spring Data JPA og Querydsl (nuværende artikel) • REST Query Language - 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 ser vi på at opbygge et forespørgselssprog til en REST API ved hjælp af Spring Data JPA og Querydsl.

I de første to artikler i denne serie byggede vi den samme søgnings- / filtreringsfunktionalitet ved hjælp af JPA Criteria og Spring Data JPA Specifications.

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 fleksibeltog giver dig mulighed for at filtrere ned til nøjagtigt de ressourcer, du har brug for.

2. Querydsl-konfiguration

Først - lad os se, hvordan vi konfigurerer vores projekt til at bruge Querydsl.

Vi skal tilføje følgende afhængigheder til pom.xml:

 com.querydsl querydsl-apt 4.2.2 com.querydsl querydsl-jpa 4.2.2 

Vi har også brug for at konfigurere APT - Annotation processing tool - plugin som følger:

 com.mysema.maven apt-maven-plugin 1.1.3 procesmål / genereret-kilder / java com.mysema.query.apt.jpa.JPAAnnotationProcessor 

Dette vil generere Q-typerne for vores enheder.

3. Den MyUser Enhed

Næste - lad os se på “MyUser”Enhed, som vi skal bruge i vores Search API:

@Entity offentlig klasse MyUser {@Id @GeneratedValue (strategi = GenerationType.AUTO) privat Lang id; privat streng fornavn; privat streng efternavn; privat streng e-mail; privat int alder }

4. Brugerdefineret Prædikat Wmed PathBuilder

Lad os nu oprette en brugerdefineret Prædikat baseret på nogle vilkårlige begrænsninger.

Vi bruger PathBuilder her i stedet for de automatisk genererede Q-typer, fordi vi er nødt til at oprette stier dynamisk for mere abstrakt brug:

offentlig klasse MyUserPredicate {private SearchCriteria kriterier; offentlig BooleanExpression getPredicate () {PathBuilder entityPath = ny PathBuilder (MyUser.class, "bruger"); if (isNumeric (criteria.getValue (). toString ())) {NumberPath path = entityPath.getNumber (criteria.getKey (), Integer.class); int-værdi = Integer.parseInt (criteria.getValue (). toString ()); switch (criteria.getOperation ()) {case ":": return path.eq (værdi); case ">": retursti.goe (værdi); sag "<": retursti.loe (værdi); }} andet {StringPath sti = entityPath.getString (kriterier.getKey ()); hvis (criteria.getOperation (). er lig medIgnoreCase (":")) {return path.containsIgnoreCase (criteria.getValue (). toString ()); }} returner null; }}

Bemærk hvordan implementeringen af ​​prædikatet er generelt beskæftiger sig med flere typer operationer. Dette skyldes, at forespørgselssproget pr. Definition er et åbent sprog, hvor du potentielt kan filtrere efter ethvert felt ved hjælp af enhver understøttet operation.

For at repræsentere den slags åbne filtreringskriterier bruger vi en simpel, men ret fleksibel implementering - Søgekriterier:

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

Det Søgekriterier holder de detaljer, vi har brug for, for at repræsentere en begrænsning:

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

5. MyUserRepository

Nu - lad os se på vores MyUserRepository.

Vi har brug for vores MyUserRepository at udvide QuerydslPredicateExecutor så vi kan bruge Predikater senere for at filtrere søgeresultaterne:

offentlig grænseflade MyUserRepository udvider JpaRepository, QuerydslPredicateExecutor, QuerydslBinderCustomizer {@Override standard public void customize (QuerydslBindings bindings, QMyUser root) {bindings.bind (String.class). first ((SingleValueBinding) Stringorepression :: bindings. ekskl. (root.email); }}

Bemærk, at vi her bruger den genererede Q-type til MyUser enhed, som vil blive navngivet QMyUser.

6. Kombiner Predikater

Næste - lad os se på at kombinere Predicates til at bruge flere begrænsninger i resultatfiltrering.

I det følgende eksempel - vi arbejder med en bygherre - MyUserPredicatesBuilder - at kombinere Predikater:

offentlig klasse MyUserPredicatesBuilder {private listeparametre; offentlig MyUserPredicatesBuilder () {params = ny ArrayList (); } public MyUserPredicatesBuilder with (String key, String operation, Object value) {params.add (new SearchCriteria (key, operation, value)); returner dette; } public BooleanExpression build () {if (params.size () == 0) {return null; } Liste over predikater = params.stream (). Kort (param -> {MyUserPredicate predicate = new MyUserPredicate (param); return predicate.getPredicate ();}). Filter (Objects :: nonNull) .collect (Collectors.toList () ); BooleanExpression result = Expressions.asBoolean (true) .isTrue (); for (BooleanExpression predicate: predicates) {result = result.and (predicate); } returnere resultat }}

7. Test søgeforespørgsler

Dernæst - lad os teste vores Search API.

Vi starter med at initialisere databasen med et par brugere - for at have disse klar og tilgængelige til test:

@RunWith (SpringJUnit4ClassRunner.class) @ContextConfiguration (classes = {PersistenceConfig.class}) @ Transactional @Rollback public class JPAQuerydslIntegrationTest {@Autowired private MyUserRepository repo; privat MyUser-brugerJohn; privat MyUser userTom; @Før offentlig ugyldig init () {userJohn = ny MyUser (); userJohn.setFirstName ("John"); userJohn.setLastName ("Doe"); userJohn.setEmail ("[email protected]"); brugerJohn.setAge (22); repo.save (userJohn); userTom = ny MyUser (); userTom.setFirstName ("Tom"); userTom.setLastName ("Doe"); userTom.setEmail ("[email protected]"); userTom.setAge (26); repo.save (userTom); }}

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

@Test offentligt ugyldigt givetLast_whenGettingListOfUsers_thenCorrect () {MyUserPredicatesBuilder builder = new MyUserPredicatesBuilder (). With ("lastName", ":", "Doe"); Iterable resultater = repo.findAll (builder.build ()); assertThat (resultater, indeholderInAnyOrder (userJohn, userTom)); }

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

@Test offentlig ugyldighed givenFirstAndLastName_whenGettingListOfUsers_thenCorrect () {MyUserPredicatesBuilder builder = new MyUserPredicatesBuilder () .with ("firstName", ":", "John"). With ("lastName", ":", "Doe"); Iterable resultater = repo.findAll (builder.build ()); assertThat (resultater, indeholder (userJohn)); assertThat (resultater, ikke (indeholder (userTom))); }

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

@Test offentlig ugyldighed givenLastAndAge_whenGettingListOfUsers_thenCorrect () {MyUserPredicatesBuilder builder = new MyUserPredicatesBuilder () .with ("lastName", ":", "Doe"). With ("age", ">", "25"); Iterable resultater = repo.findAll (builder.build ()); assertThat (resultater, indeholder (userTom)); assertThat (resultater, ikke (indeholder (userJohn))); }

Lad os nu se, hvordan vi søger efter MyUser at eksisterer faktisk ikke:

@Test offentlig ugyldighed givenWrongFirstAndLast_whenGettingListOfUsers_thenCorrect () {MyUserPredicatesBuilder builder = new MyUserPredicatesBuilder () .with ("firstName", ":", "Adam"). With ("lastName", ":", "Fox"); Iterable resultater = repo.findAll (builder.build ()); assertThat (results, emptyIterable ()); }

Endelig - lad os se, hvordan man finder en MyUser kun givet en del af fornavnet - som i følgende eksempel:

@Test offentlig ugyldighed givenPartialFirst_whenGettingListOfUsers_thenCorrect () {MyUserPredicatesBuilder builder = new MyUserPredicatesBuilder (). With ("firstName", ":", "jo"); Iterable resultater = repo.findAll (builder.build ()); assertThat (resultater, indeholder (userJohn)); assertThat (resultater, ikke (indeholder (userTom))); }

8. UserController

Lad os endelig sætte alt sammen og bygge REST API.

Vi definerer en UserController der definerer en enkel metode findAll () med en "Søg"Parameter, der skal sendes i forespørgselsstrengen:

@Controller offentlig klasse UserController {@Autowired private MyUserRepository myUserRepository; @RequestMapping (method = RequestMethod.GET, value = "/ myusers") @ResponseBody public Iterable search (@RequestParam (value = "search") String search) {MyUserPredicatesBuilder builder = new MyUserPredicatesBuilder (); hvis (søg! = null) {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)); }} BooleanExpression exp = builder.build (); returner myUserRepository.findAll (exp); }}

Her er et eksempel på en hurtig test-URL:

// localhost: 8080 / myusers? search = lastName: doe, alder> 25

Og svaret:

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

9. Konklusion

Denne tredje artikel dækket de første trin i opbygningen af ​​et forespørgselssprog til en REST API, gør god brug af Querydsl-biblioteket.

Implementeringen er naturligvis tidligt, men den kan let udvikles til at understøtte yderligere 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-forespørgselssprog - Avanceret søgning « Tidligere REST-forespørgselssprog med Spring Data JPA-specifikationer

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