Bahasa Pertanyaan REST dengan Kriteria Musim Semi dan JPA

REST Teratas

Saya baru sahaja mengumumkan kursus Learn Spring yang baru , yang berfokus pada asas-asas Spring 5 dan Spring Boot 2:

>> SEMAK KURSUS Artikel ini adalah sebahagian daripada siri: • REST Query Language with Spring dan JPA Kriteria (artikel semasa) • REST Query Language dengan Spring Data Spesifikasi JPA

• REST Query Language dengan Spring Data JPA dan Querydsl

• REST Query Language - Operasi Carian Lanjutan

• Bahasa Pertanyaan REST - Melaksanakan ATAU Operasi

• REST Query Language dengan RSQL

• REST Query Language dengan Sokongan Web Querydsl

1. Gambaran keseluruhan

Dalam artikel pertama siri baru ini, kami akan meneroka bahasa pertanyaan mudah untuk REST API . Kami akan menggunakan Spring dengan baik untuk REST API dan JPA 2 Kriteria untuk aspek kegigihan.

Mengapa bahasa pertanyaan? Kerana - untuk API yang cukup rumit - mencari / menapis sumber anda dengan medan yang sangat sederhana tidak cukup. Bahasa pertanyaan lebih fleksibel, dan membolehkan anda menapis sumber yang anda perlukan.

2. Entiti Pengguna

Pertama - mari kita kemukakan entiti sederhana yang akan kita gunakan untuk API penapis / carian kita - Pengguna asas :

@Entity public class User { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; private String firstName; private String lastName; private String email; private int age; }

3. Tapis Menggunakan CriteriaBuilder

Sekarang - mari masuk ke dalam masalah masalah - pertanyaan dalam lapisan ketekunan.

Membina abstraksi pertanyaan adalah soal keseimbangan. Kita memerlukan banyak fleksibiliti di satu pihak, dan kita perlu memastikan kerumitan dapat dikendalikan di pihak lain. Tahap tinggi, fungsinya sederhana - anda melewati beberapa kekangan dan anda mendapat kembali beberapa hasil .

Mari lihat bagaimana ia berfungsi:

@Repository public class UserDAO implements IUserDAO { @PersistenceContext private EntityManager entityManager; @Override public List searchUser(List params) { CriteriaBuilder builder = entityManager.getCriteriaBuilder(); CriteriaQuery query = builder.createQuery(User.class); Root r = query.from(User.class); Predicate predicate = builder.conjunction(); UserSearchQueryCriteriaConsumer searchConsumer = new UserSearchQueryCriteriaConsumer(predicate, builder, r); params.stream().forEach(searchConsumer); predicate = searchConsumer.getPredicate(); query.where(predicate); List result = entityManager.createQuery(query).getResultList(); return result; } @Override public void save(User entity) { entityManager.persist(entity); } }

Mari lihat kelas UserSearchQueryCriteriaConsumer :

public class UserSearchQueryCriteriaConsumer implements Consumer{ private Predicate predicate; private CriteriaBuilder builder; private 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(predicate, builder.lessThanOrEqualTo( r.get(param.getKey()), param.getValue().toString())); } else if (param.getOperation().equalsIgnoreCase(":")) { if (r.get(param.getKey()).getJavaType() == String.class) { predicate = builder.and(predicate, builder.like( r.get(param.getKey()), "%" + param.getValue() + "%")); } else { predicate = builder.and(predicate, builder.equal( r.get(param.getKey()), param.getValue())); } } } // standard constructor, getter, setter }

Seperti yang anda lihat, API searchUser mengambil senarai kekangan yang sangat mudah, menyusun pertanyaan berdasarkan kekangan ini, melakukan carian dan mengembalikan hasilnya.

Kelas kekangan juga mudah:

public class SearchCriteria { private String key; private String operation; private Object value; }

The SearchCriteria pelaksanaan memegang kami Pertanyaan parameter:

  • kunci : digunakan untuk memegang nama bidang - contohnya: nama pertama , umur , ... dll.
  • operasi : digunakan untuk menahan operasi - contohnya: Kesamaan, kurang daripada, ... dll.
  • nilai : digunakan untuk menahan nilai medan - contohnya: john, 25,… dll.

4. Uji Pertanyaan Carian

Sekarang - mari kita uji mekanisme carian kami untuk memastikan ia menahan air.

Pertama - mari kita mulakan pangkalan data kami untuk diuji dengan menambahkan dua pengguna - seperti dalam contoh berikut:

@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = { PersistenceConfig.class }) @Transactional @TransactionConfiguration public class JPACriteriaQueryTest { @Autowired private IUserDAO userApi; private User userJohn; private User userTom; @Before public void init() { userJohn = new User(); userJohn.setFirstName("John"); userJohn.setLastName("Doe"); userJohn.setEmail("[email protected]"); userJohn.setAge(22); userApi.save(userJohn); userTom = new User(); userTom.setFirstName("Tom"); userTom.setLastName("Doe"); userTom.setEmail("[email protected]"); userTom.setAge(26); userApi.save(userTom); } }

Sekarang, mari kita mendapatkan pengguna dengan tertentu firstName dan LASTNAME - seperti dalam contoh berikut:

@Test public void givenFirstAndLastName_whenGettingListOfUsers_thenCorrect() { List params = new ArrayList(); params.add(new SearchCriteria("firstName", ":", "John")); params.add(new SearchCriteria("lastName", ":", "Doe")); List results = userApi.searchUser(params); assertThat(userJohn, isIn(results)); assertThat(userTom, not(isIn(results))); }

Seterusnya, mari kita mendapatkan Senarai daripada pengguna dengan yang sama Akhir Penerima :

@Test public void givenLast_whenGettingListOfUsers_thenCorrect() { List params = new ArrayList(); params.add(new SearchCriteria("lastName", ":", "Doe")); List results = userApi.searchUser(params); assertThat(userJohn, isIn(results)); assertThat(userTom, isIn(results)); }

Seterusnya, mari dapatkan pengguna dengan usia lebih besar atau sama dengan 25 :

@Test public void givenLastAndAge_whenGettingListOfUsers_thenCorrect() { List params = new ArrayList(); params.add(new SearchCriteria("lastName", ":", "Doe")); params.add(new SearchCriteria("age", ">", "25")); List results = userApi.searchUser(params); assertThat(userTom, isIn(results)); assertThat(userJohn, not(isIn(results))); }

Seterusnya, mari cari pengguna yang sebenarnya tidak ada :

@Test public void givenWrongFirstAndLast_whenGettingListOfUsers_thenCorrect() { List params = new ArrayList(); params.add(new SearchCriteria("firstName", ":", "Adam")); params.add(new SearchCriteria("lastName", ":", "Fox")); List results = userApi.searchUser(params); assertThat(userJohn, not(isIn(results))); assertThat(userTom, not(isIn(results))); }

Akhir sekali, carian let untuk pengguna diberikan hanya separa firstName :

@Test public void givenPartialFirst_whenGettingListOfUsers_thenCorrect() { List params = new ArrayList(); params.add(new SearchCriteria("firstName", ":", "jo")); List results = userApi.searchUser(params); assertThat(userJohn, isIn(results)); assertThat(userTom, not(isIn(results))); }

6. Pengawal Pengguna

Akhirnya, mari kita ikuti sokongan berterusan untuk carian fleksibel ini ke REST API kami.

Kami akan menyediakan UserController sederhana - dengan findAll () menggunakan " search " untuk menyampaikan keseluruhan ekspresi carian / penapis :

@Controller public class UserController { @Autowired private IUserDao api; @RequestMapping(method = RequestMethod.GET, value = "/users") @ResponseBody public List findAll(@RequestParam(value = "search", required = false) String search) { List params = new ArrayList(); if (search != null) { Pattern pattern = Pattern.compile("(\w+?)(:|)(\w+?),"); Matcher matcher = pattern.matcher(search + ","); while (matcher.find()) { params.add(new SearchCriteria(matcher.group(1), matcher.group(2), matcher.group(3))); } } return api.searchUser(params); } }

Perhatikan bagaimana kita hanya membuat objek kriteria carian kita daripada ungkapan carian.

Kami sekarang berada di tahap di mana kita boleh mula bermain dengan API dan memastikan semuanya berfungsi dengan betul:

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

Dan inilah tindak balasnya:

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

7. Kesimpulannya

Pelaksanaan yang sederhana namun kuat ini membolehkan penapisan pintar pada REST API. Ya - ia masih kasar dan boleh diperbaiki (dan akan diperbaiki dalam artikel seterusnya) - tetapi ini adalah titik permulaan yang kukuh untuk melaksanakan fungsi penapisan seperti ini pada API anda.

The pelaksanaan penuh artikel ini boleh didapati dalam projek GitHub - ini adalah projek berasaskan Maven, jadi ia harus mudah untuk import dan berjalan kerana ia adalah.

Seterusnya » Bahasa Pertanyaan REST dengan Spesifikasi JPA Spring Data REST bawah

Saya baru sahaja mengumumkan kursus Learn Spring yang baru , yang berfokus pada asas-asas Spring 5 dan Spring Boot 2:

>> SEMAK KURSUS