REST Query Language med vårdata JPA og Querydsl

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

• REST-spørrespråk med vårdata JPA-spesifikasjoner

• REST Query Language with Spring Data JPA and Querydsl (nåværende artikkel) • REST Query Language - Advanced Search Operations

• REST Query Language - Implementering ELLER drift

• REST Query Language med RSQL

• REST Query Language med Querydsl Web Support

1. Oversikt

I denne opplæringen ser vi på å bygge et spørrespråk for en REST API ved hjelp av Spring Data JPA og Querydsl.

I de to første artiklene i denne serien bygde vi den samme søke- / filtreringsfunksjonaliteten ved hjelp av JPA Criteria og Spring Data JPA Specifications.

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

2. Querydsl-konfigurasjon

Først - la oss se hvordan vi konfigurerer prosjektet vårt til å bruke Querydsl.

Vi må legge til følgende avhengigheter til pom.xml:

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

Vi må også konfigurere APT - Annotation processing tool - plugin som følger:

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

Dette vil generere Q-typene for enhetene våre.

3. Den Min bruker Enhet

Neste - la oss ta en titt på “Min bruker”Enhet som vi skal bruke i vårt søke-API:

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

4. Tilpasset Predikat With PathBuilder

Nå - la oss lage en tilpasset Predikere basert på noen vilkårlige begrensninger.

Vi bruker PathBuilder her i stedet for de automatisk genererte Q-typene fordi vi trenger å lage stier dynamisk for mer abstrakt bruk:

offentlig klasse MyUserPredicate {private SearchCriteria kriterier; public BooleanExpression getPredicate () {PathBuilder entityPath = new PathBuilder (MyUser.class, "user"); if (isNumeric (criteria.getValue (). toString ())) {NumberPath path = entityPath.getNumber (criteria.getKey (), Integer.class); int-verdi = Integer.parseInt (criteria.getValue (). toString ()); switch (criteria.getOperation ()) {case ":": return path.eq (verdi); case ">": retursti.goe (verdi); sak "<": retursti.loe (verdi); }} annet {StringPath path = entityPath.getString (criteria.getKey ()); if (criteria.getOperation (). equalsIgnoreCase (":")) {return path.containsIgnoreCase (criteria.getValue (). toString ()); }} returner null; }}

Legg merke til hvordan implementeringen av predikatet er generelt å håndtere flere typer operasjoner. Dette er fordi spørringsspråket per definisjon er et åpent språk der du potensielt kan filtrere etter hvilket som helst felt ved hjelp av en hvilken som helst operasjon som støttes.

For å representere den typen åpne filtreringskriterier bruker vi en enkel, men ganske fleksibel implementering - Søkekriterier:

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

De Søkekriterier har detaljene vi trenger for å representere en begrensning:

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

5. MyUserRepository

Nå - la oss ta en titt på vår MyUserRepository.

Vi trenger vår MyUserRepository å forlenge QuerydslPredicateExecutor slik at vi kan bruke Predikater senere for å filtrere søkeresultatene:

offentlig grensesnitt MyUserRepository utvider JpaRepository, QuerydslPredicateExecutor, QuerydslBinderCustomizer {@Override standard public void customize (QuerydslBindings bindings, QMyUser root) {bindings.bind (String.class). first ((SingleValueBinding) Stringorepression :: inneholder bindings. eksklusiv (root.email); }}

Merk at vi bruker her den genererte Q-typen for Min bruker enhet, som vil bli navngitt QMyUser.

6. Kombiner Predikater

Neste - la oss ta en titt på å kombinere predikater for å bruke flere begrensninger i resultatfiltrering.

I det følgende eksemplet - vi jobber med en byggherre - MyUserPredicatesBuilder - Å kombinere Predikater:

offentlig klasse MyUserPredicatesBuilder {private List params; public MyUserPredicatesBuilder () {params = new ArrayList (); } offentlig MyUserPredicatesBuilder med (strengnøkkel, strengoperasjon, objektverdi) {params.add (ny SearchCriteria (nøkkel, operasjon, verdi)); returner dette; } public BooleanExpression build () {if (params.size () == 0) {return null; } Liste predikater = params.stream (). Kart (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økene

Neste - la oss teste søke-API-et vårt.

Vi starter med å initialisere databasen med noen få brukere - for å ha disse klare og tilgjengelige for testing:

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

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

@Test offentlig ugyldig gittLast_whenGettingListOfUsers_thenCorrect () {MyUserPredicatesBuilder builder = new MyUserPredicatesBuilder (). With ("lastName", ":", "Doe"); Iterable resultater = repo.findAll (builder.build ()); assertThat (resultater, inneholderInAnyOrder (userJohn, userTom)); }

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

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

La oss deretter se hvordan vi kan finne bruker med gitt både etternavn og aldersgrense

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

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

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

Til slutt - la oss se hvordan du finner en Min bruker bare gitt en del av fornavnet - som i følgende eksempel:

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

8. UserController

Til slutt, la oss sette alt sammen og bygge REST API.

Vi definerer en UserController som definerer en enkel metode findAll () med en "Søk“Parameter som skal sendes i spørringsstrengen:

@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øk! = null) {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)); }} BooleanExpression exp = builder.build (); returner myUserRepository.findAll (exp); }}

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

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

Og svaret:

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

9. Konklusjon

Denne tredje artikkelen dekket de første trinnene i å bygge et spørrespråk for et REST API, som bruker Querydsl-biblioteket godt.

Implementeringen er selvfølgelig tidlig, men den kan lett utvikles for å støtte ytterligere 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 - Avansert søkeoperasjon « Forrige REST-spørrespråk med vårdata JPA-spesifikasjoner

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