REST upitni jezik s RSQL-om
Upravo sam najavio novo Uči proljeće tečaj, usredotočen na osnove Spring 5 i Spring Boot 2:
>> PROVJERITE TEČAJ Vrh postojanostiUpravo sam najavio novo Uči proljeće tečaj, usredotočen na osnove Spring 5 i Spring Boot 2:
>> PROVJERITE TEČAJ Ovaj je članak dio serije: • REST jezik upita s proljetnim i JPA kriterijima• REST jezik upita s Spring Data JPA specifikacijama
• REST jezik upita s Spring Data JPA i Querydsl
• REST jezik upita - Napredno pretraživanje
• REST upitni jezik - implementacija ILI operacija
• REST upitni jezik s RSQL-om (trenutni članak) • REST upitni jezik s web-podrškom Querydsl
1. Pregled
U ovom petom članku serije ilustrirat ćemo izgradnju jezika upita REST API uz pomoć cool knjižnica - rsql-parser.
RSQL je super set jezika upita stavki feed-a (FIQL) - čista i jednostavna sintaksa filtra za feedove; tako da se sasvim prirodno uklapa u REST API. Prvo, dodamo maven ovisnost u knjižnicu: I također definirati glavni entitet radit ćemo na svim primjerima - Korisnik: Način na koji su RSQL izrazi interno predstavljeni u obliku je čvorova, a obrazac posjetitelja koristi se za raščlanjivanje ulaza. Imajući to na umu, mi ćemo implementirati RSQLVisitor sučelje i stvorimo vlastitu implementaciju posjetitelja - CustomRsqlVisitor: Sada se moramo nositi s upornošću i konstruirati svoj upit iz svakog od tih čvorova. Koristit ćemo Spring Data JPA specifikacije koje smo koristili prije - i implementirat ćemo a Specifikacija graditelj do konstruirajte Specifikacije iz svakog od ovih čvorova koje posjetimo: Imajte na umu kako: Na primjer, za upit "ime == Ivan" - imamo: Prilikom izrade upita koristili smo a Specifikacija: Primijetite kako se specifikacija koristi generičkim lijekovima i nije vezana uz bilo koji entitet (kao što je Korisnik). Dalje - evo našeg nabrajati “RsqlSearchOperation“ koji sadrži zadane rsql-parser operatore: Počnimo sada testirati naše nove i fleksibilne operacije kroz neke stvarne scenarije: Prvo - inicijalizirajmo podatke: Sada testirajmo različite operacije: U sljedećem primjeru - tražit ćemo korisnike prema njihovim prvi i prezime: Dalje, potražimo korisnike koji prema njihovim ime ne "john": Dalje - tražit ćemo korisnike s dob veće od “25”: Dalje - tražit ćemo korisnike sa svojim ime počevši sa "jo”: Dalje - tražit ćemo korisnike njihove ime je "Ivan" ili "utičnica“: Napokon - povežimo sve s kontrolerom: Evo primjera URL-a: I odgovor: Ovaj je vodič ilustrirao kako izraditi jezik upita / pretraživanja za REST API bez potrebe za ponovnim izmišljanjem sintakse i umjesto toga pomoću FIQL / RSQL. The puna provedba ovog članka može se naći u projektu GitHub - ovo je projekt zasnovan na Mavenu, pa bi ga trebalo lako uvesti i pokrenuti kakav jest.2. Pripreme
cz.jirutka.rsql rsql-parser 2.1.0
@Entity javna klasa Korisnik {@Id @GeneratedValue (strategy = GenerationType.AUTO) private Long id; private String firstName; private String lastName; privatni String e-mail; privatno int doba; }
3. Analizirajte zahtjev
javna klasa CustomRsqlVisitor implementira RSQLVisitor
javna klasa GenericRsqlSpecBuilder {javna specifikacija createSpecification (čvor čvora) {if (čvor instanceof LogicalNode) {return createSpecification ((LogicalNode) čvor); } if (čvor instanceof ComparisonNode) {return createSpecification ((ComparisonNode) čvor); } return null; } javna specifikacija createSpecification (LogicalNode logicNode) {Specifikacije popisa = logicNode.getChildren () .stream () .map (node -> createSpecification (node)) .filter (Objects :: nonNull) .collect (Collectors.toList ()); Rezultat specifikacije = specs.get (0); if (logicNode.getOperator () == LogicalOperator.AND) {for (int i = 1; i <specs.size (); i ++) {rezultat = Specifikacija.gdje (rezultat) .i (specs.get (i)) ; }} else if (logicNode.getOperator () == LogicalOperator.OR) {for (int i = 1; i <specs.size (); i ++) {result = Specification.where (result) .or (specs.get ( i)); }} vratiti rezultat; } javna specifikacija createSpecification (ComparisonNode порівняvanjeNode) {Rezultat specifikacije = Specifikacija.gdje (nova GenericRsqlSpecification (порівняvanjeNode.getSelector (), uspoređivanjeNode.getOperator (), uspoređivanjeNode.getArguments ())); povratni rezultat; }}
4. Stvorite prilagođeno Specifikacija
javna klasa GenericRsqlSpecification implementira specifikaciju {private String property; privatni operator ComparisonOperator; argumenti privatnog popisa; @Override public Predicate toPredicate (root root, CriteriaQuery query, CriteriaBuilder builder) {List args = castArguments (root); Argument objekta = args.get (0); switch (RsqlSearchOperation.getSimpleOperator (operator)) {case EQUAL: {if (argument instanceof String) {return builder.like (root.get (svojstvo), argument.toString (). replace ('*', '%')) ; } else if (argument == null) {return builder.isNull (root.get (svojstvo)); } else {vratiti graditelj.equal (root.get (svojstvo), argument); }} slučaj NOT_EQUAL: {if (argument instanceof String) {return builder.notLike (root. get (svojstvo), argument.toString (). replace ('*', '%')); } else if (argument == null) {return builder.isNotNull (root.get (svojstvo)); } else {return builder.notEqual (root.get (svojstvo), argument); }} case GREATER_THAN: {return builder.greaterThan (root. get (property), argument.toString ()); } slučaj GREATER_THAN_OR_EQUAL: {return builder.greaterThanOrEqualTo (root. get (svojstvo), argument.toString ()); } case LESS_THAN: {return builder.lessThan (root. get (property), argument.toString ()); } slučaj LESS_THAN_OR_EQUAL: {return builder.lessThanOrEqualTo (root. get (svojstvo), argument.toString ()); } case IN: vratiti root.get (svojstvo) .in (args); case NOT_IN: return builder.not (root.get (svojstvo) .in (args)); } return null; } privatni popis castArguments (konačni korijenski korijen) {Vrsta klase = root.get (svojstvo) .getJavaType (); Navedi args = argument.stream (). Map (arg -> {if (type.equals (Integer.class)) {return Integer.parseInt (arg);} else if (type.equals (Long.class)) {return Long.parseLong (arg);} else {return arg;}}). Collect (Collectors.toList ()); vratiti argumente; } // standardni konstruktor, getter, setter}
javni enum {RsqlSearchOperation jednako (RSQLOperators.EQUAL), NOT_EQUAL (RSQLOperators.NOT_EQUAL), GREATER_THAN (RSQLOperators.GREATER_THAN), GREATER_THAN_OR_EQUAL (RSQLOperators.GREATER_THAN_OR_EQUAL), LESS_THAN (RSQLOperators.LESS_THAN), LESS_THAN_OR_EQUAL (RSQLOperators.LESS_THAN_OR_EQUAL), IN (RSQLOperators. IN), NOT_IN (RSQLOperators.NOT_IN); privatni operator ComparisonOperator; private RsqlSearchOperation (operator ComparisonOperator) {this.operator = operator; } javni statički RsqlSearchOperation getSimpleOperator (operator ComparisonOperator) {za (operacija RsqlSearchOperation: values ()) {if (operation.getOperator () == operator) {return operation; }} return null; }}
5. Testirajte upite za pretraživanje
@RunWith (SpringJUnit4ClassRunner.class) @ContextConfiguration (classes = {PersistenceConfig.class}) @Transactional @TransactionConfiguration javna klasa RsqlTest {@Autowired private UserRepository repository; privatni korisnik userJohn; privatni korisnik userTom; @Prije javne void init () {userJohn = novi korisnik (); userJohn.setFirstName ("john"); userJohn.setLastName ("srna"); userJohn.setEmail ("[e-pošta zaštićena]"); userJohn.setAge (22); repository.save (userJohn); userTom = novi korisnik (); userTom.setFirstName ("tom"); userTom.setLastName ("srna"); userTom.setEmail ("[e-pošta zaštićena]"); userTom.setAge (26); repository.save (userTom); }}
5.1. Testirajte jednakost
@Test public void givenFirstAndLastName_whenGettingListOfUsers_thenCorrect () {Node rootNode = new RSQLParser (). Parse ("firstName == john; lastName == doe"); Specifikacija spec = rootNode.accept (novi CustomRsqlVisitor ()); Popis rezultata = repozitorij.findAll (spec.); assertThat (userJohn, isIn (rezultati)); assertThat (userTom, ne (isIn (rezultati))); }
5.2. Test negacija
@Test javna praznina givenFirstNameInverse_whenGettingListOfUsers_thenCorrect () {Node rootNode = new RSQLParser (). Parse ("firstName! = John"); Specifikacija spec = rootNode.accept (novi CustomRsqlVisitor ()); Popis rezultata = repozitorij.findAll (spec.); assertThat (userTom, isIn (rezultati)); assertThat (userJohn, ne (isIn (rezultati))); }
5.3. Testirajte veće od
@Test javna praznina givenMinAge_whenGettingListOfUsers_thenCorrect () {Node rootNode = new RSQLParser (). Parse ("age> 25"); Specifikacija spec = rootNode.accept (novi CustomRsqlVisitor ()); Popis rezultata = repozitorij.findAll (spec.); assertThat (userTom, isIn (rezultati)); assertThat (userJohn, ne (isIn (rezultati))); }
5.4. Testirajte lajk
@Test javna praznina givenFirstNamePrefix_whenGettingListOfUsers_thenCorrect () {Node rootNode = new RSQLParser (). Parse ("firstName == jo *"); Specifikacija spec = rootNode.accept (novi CustomRsqlVisitor ()); Popis rezultata = repozitorij.findAll (spec.); assertThat (userJohn, isIn (rezultati)); assertThat (userTom, ne (isIn (rezultati))); }
5.5. Test U
@Test javna praznina givenListOfFirstName_whenGettingListOfUsers_thenCorrect () {Node rootNode = new RSQLParser (). Parse ("firstName = in = (john, jack)"); Specifikacija spec = rootNode.accept (novi CustomRsqlVisitor ()); Popis rezultata = repozitorij.findAll (spec.); assertThat (userJohn, isIn (rezultati)); assertThat (userTom, ne (isIn (rezultati))); }
6. UserController
@RequestMapping (method = RequestMethod.GET, value = "/ users") @ResponseBody javni popis findAllByRsql (@RequestParam (value = "search") String search) {Node rootNode = new RSQLParser (). Parse (search); Specifikacija spec = rootNode.accept (novi CustomRsqlVisitor ()); return dao.findAll (spec); }
// localhost: 8080 / users? search = firstName == jo *; dob <25
[{"id": 1, "firstName": "john", "lastName": "doe", "email": "[email protected]", "age": 24}]
7. Zaključak
Upravo sam najavio novo Uči proljeće tečaj, usredotočen na osnove Spring 5 i Spring Boot 2:
>> PROVJERITE TEČAJ Dno postojanosti Upravo sam najavio novo Uči proljeće tečaj, usredotočen na osnove Spring 5 i Spring Boot 2:
>> PROVJERITE TEČAJ