REST Query Language met Spring- en JPA-criteria

REST Top

Ik heb zojuist het nieuwe aangekondigd Leer de lente natuurlijk, gericht op de basisprincipes van Spring 5 en Spring Boot 2:

>> BEKIJK DE CURSUS Dit artikel maakt deel uit van een reeks: • REST Query Language met Spring- en JPA-criteria (huidig ​​artikel) • REST Query-taal met Spring Data JPA-specificaties

• REST Query Language met Spring Data JPA en Querydsl

• REST Query Language - Geavanceerde zoekbewerkingen

• REST Query Language - OR-bewerking implementeren

• REST Query Language met RSQL

• REST Query-taal met Querydsl-webondersteuning

1. Overzicht

In dit eerste artikel van deze nieuwe serie gaan we het verkennen een eenvoudige zoektaal voor een REST API. We maken goed gebruik van Spring voor de REST API en JPA 2 Criteria voor de persistentieaspecten.

Waarom een ​​zoektaal? Omdat - voor elke voldoende complexe API - het doorzoeken / filteren van uw bronnen op zeer eenvoudige velden gewoon niet voldoende is. Een zoektaal is flexibeler en stelt u in staat om te filteren op precies de bronnen die u nodig heeft.

2. Gebruiker Entiteit

Laten we eerst de eenvoudige entiteit naar voren brengen die we gaan gebruiken voor onze filter- / zoek-API - een basiselement Gebruiker:

@Entity openbare klasse Gebruiker {@Id @GeneratedValue (strategie = GenerationType.AUTO) privé Lange id; private String voornaam; private String achternaam; privé String-e-mail; privé int leeftijd; }

3. Filter gebruiken CriteriaBuilder

Laten we nu eens kijken naar de kern van het probleem - de vraag in de persistentielaag.

Het opbouwen van een query-abstractie is een kwestie van evenwicht. We hebben enerzijds een behoorlijke mate van flexibiliteit nodig, en anderzijds moeten we de complexiteit beheersbaar houden. Hoog niveau, de functionaliteit is eenvoudig - u passeert enkele beperkingen en u krijgt enkele resultaten terug.

Laten we eens kijken hoe dat werkt:

@Repository openbare klasse UserDAO implementeert IUserDAO {@PersistenceContext private EntityManager entityManager; @Override openbare lijst searchUser (lijstparameters) {CriteriaBuilder builder = entityManager.getCriteriaBuilder (); CriteriaQuery-query = builder.createQuery (User.class); Root r = query.from (User.class); Predikaat predikaat = builder.conjunction (); UserSearchQueryCriteriaConsumer searchConsumer = nieuwe UserSearchQueryCriteriaConsumer (predikaat, builder, r); params.stream (). forEach (searchConsumer); predicate = searchConsumer.getPredicate (); query.where (predikaat); Lijstresultaat = entityManager.createQuery (query) .getResultList (); resultaat teruggeven; } @Override public void save (User entity) {entityManager.persist (entity); }}

Laten we eens kijken naar de UserSearchQueryCriteriaConsumer klasse:

openbare klasse UserSearchQueryCriteriaConsumer implementeert Consumer {private Predicaat-predikaat; private CriteriaBuilder-bouwer; privé Root r; @Override public void accept (SearchCriteria param) {if (param.getOperation (). EqualsIgnoreCase (">")) {predicate = builder.and (predicate, builder .greaterThanOrEqualTo (r.get (param.getKey ())), param .getValue (). toString ())); } else if (param.getOperation (). equalsIgnoreCase ("<")) {predicate = builder.and (predicaat, builder.lessThanOrEqualTo (r.get (param.getKey ()), param.getValue (). toString () )); } else if (param.getOperation (). equalsIgnoreCase (":")) {if (r.get (param.getKey ()). getJavaType () == String.class) {predicaat = builder.and (predikaat, builder .like (r.get (param.getKey ()), "%" + param.getValue () + "%")); } else {predikaat = builder.and (predikaat, builder.equal (r.get (param.getKey ()), param.getValue ())); }}} // standard constructor, getter, setter}

Zoals u kunt zien, is de searchUser API neemt een lijst met zeer eenvoudige beperkingen, stelt een query samen op basis van deze beperkingen, voert de zoekopdracht uit en retourneert de resultaten.

De constraint-klasse is ook vrij eenvoudig:

openbare klasse SearchCriteria {privé String-sleutel; privé String-bewerking; waarde van het privé-object; }

De Zoekcriteria implementatie houdt onze Vraag parameters:

  • sleutel: wordt gebruikt om veldnaam vast te houden - bijvoorbeeld: Voornaam, leeftijd, … enz.
  • operatie: wordt gebruikt om de bewerking vast te houden - bijvoorbeeld: gelijkheid, minder dan, ... enz.
  • waarde: wordt gebruikt om de veldwaarde vast te houden - bijvoorbeeld: john, 25, ... enz.

4. Test de zoekopdrachten

Laten we nu ons zoekmechanisme testen om er zeker van te zijn dat het water vasthoudt.

Laten we eerst onze database initialiseren voor testen door twee gebruikers toe te voegen - zoals in het volgende voorbeeld:

@RunWith (SpringJUnit4ClassRunner.class) @ContextConfiguration (classes = {PersistenceConfig.class}) @Transactional @TransactionConfiguration openbare klasse JPACriteriaQueryTest {@Autowired private IUserDAO userApi; privé gebruiker userJohn; particuliere gebruiker userTom; @Before public void init () {userJohn = nieuwe gebruiker (); userJohn.setFirstName ("John"); userJohn.setLastName ("Doe"); userJohn.setEmail ("[e-mail beschermd]"); userJohn.setAge (22); userApi.save (userJohn); userTom = nieuwe gebruiker (); userTom.setFirstName ("Tom"); userTom.setLastName ("Doe"); userTom.setEmail ("[e-mail beschermd]"); userTom.setAge (26); userApi.save (userTom); }}

Laten we nu een Gebruiker met specifieke Voornaam en achternaam - zoals in het volgende voorbeeld:

@Test openbare leegte gegevenFirstAndLastName_whenGettingListOfUsers_thenCorrect () {Lijstparameters = nieuwe ArrayList (); params.add (nieuwe zoekcriteria ("firstName", ":", "John")); params.add (nieuwe zoekcriteria ("achternaam", ":", "Doe")); Lijstresultaten = userApi.searchUser (params); assertThat (userJohn, isIn (resultaten)); assertThat (userTom, niet (isIn (resultaten))); }

Laten we vervolgens een Lijst van Gebruiker met hetzelfde achternaam:

@Test openbare ongeldig gegevenLast_whenGettingListOfUsers_thenCorrect () {Lijstparameters = nieuwe ArrayList (); params.add (nieuwe zoekcriteria ("achternaam", ":", "Doe")); Lijstresultaten = userApi.searchUser (params); assertThat (userJohn, isIn (resultaten)); assertThat (userTom, isIn (resultaten)); }

Laten we vervolgens gebruikers krijgen met leeftijdgroter dan of gelijk aan 25:

@Test openbare leegte gegevenLastAndAge_whenGettingListOfUsers_thenCorrect () {Lijstparameters = nieuwe ArrayList (); params.add (nieuwe zoekcriteria ("achternaam", ":", "Doe")); params.add (nieuwe zoekcriteria ("leeftijd", ">", "25")); Lijstresultaten = userApi.searchUser (params); assertThat (userTom, isIn (resultaten)); assertThat (userJohn, not (isIn (results))); }

Laten we vervolgens zoeken naar gebruikers die bestaan ​​niet echt:

@Test openbare leegte gegevenWrongFirstAndLast_whenGettingListOfUsers_thenCorrect () {Lijstparameters = nieuwe ArrayList (); params.add (nieuwe zoekcriteria ("firstName", ":", "Adam")); params.add (nieuwe zoekcriteria ("achternaam", ":", "Fox")); Lijstresultaten = userApi.searchUser (params); assertThat (userJohn, not (isIn (results))); assertThat (userTom, niet (isIn (resultaten))); }

Laten we tot slot zoeken naar alleen opgegeven gebruikers gedeeltelijkVoornaam:

@Test openbare leegte gegevenPartialFirst_whenGettingListOfUsers_thenCorrect () {Lijstparameters = nieuwe ArrayList (); params.add (nieuwe zoekcriteria ("voornaam", ":", "jo")); Lijstresultaten = userApi.searchUser (params); assertThat (userJohn, isIn (resultaten)); assertThat (userTom, niet (isIn (resultaten))); }

6. Het UserController

Laten we tot slot nu de persistentie-ondersteuning voor deze flexibele zoekopdracht doorverbinden met onze REST API.

We gaan een eenvoudig opzetten UserController - met een vind alle()de ... gebruiken "zoeken”Om de volledige zoek- / filteruitdrukking door te geven:

@Controller openbare klasse UserController {@Autowired privé IUserbao api; @RequestMapping (methode = RequestMethod.GET, value = "/ gebruikers") @ResponseBody openbare lijst findAll (@RequestParam (waarde = "zoeken", vereist = false) String zoeken) {Lijstparameters = nieuwe ArrayList (); if (zoek! = null) {Patroonpatroon = Patroon.compile ("(\ w +?) (: |) (\ w +?),"); Matcher matcher = pattern.matcher (zoek + ","); while (matcher.find ()) {params.add (nieuwe zoekcriteria (matcher.group (1), matcher.group (2), matcher.group (3))); }} retourneer api.searchUser (params); }}

Merk op hoe we eenvoudigweg onze zoekcriteriumobjecten maken uit de zoekuitdrukking.

We zijn nu op het punt waarop we kunnen beginnen met spelen met de API en ervoor kunnen zorgen dat alles correct werkt:

// localhost: 8080 / gebruikers? search = achternaam: doe, leeftijd> 25

En hier is zijn antwoord:

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

7. Conclusie

Deze eenvoudige maar krachtige implementatie maakt behoorlijk wat slimme filtering op een REST API mogelijk. Ja - het is nog steeds ruw aan de randen en kan worden verbeterd (en zal in het volgende artikel worden verbeterd) - maar het is een solide startpunt om dit soort filterfunctionaliteit op uw API's te implementeren.

De volledige implementatie van dit artikel is te vinden in het GitHub-project - dit is een op Maven gebaseerd project, dus het zou gemakkelijk te importeren en uit te voeren moeten zijn zoals het is.

De volgende » REST Query Language met Spring Data JPA Specificaties REST onder

Ik heb zojuist het nieuwe aangekondigd Leer de lente natuurlijk, gericht op de basisprincipes van Spring 5 en Spring Boot 2:

>> BEKIJK DE CURSUS

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