שפת שאילתת REST עם מפרט JPA של נתוני אביב

מאמר זה הוא חלק מסדרה: • שפת שאילתות REST עם קריטריוני אביב ו- JPA

• שפת שאילתת REST עם מפרטי JPA של נתוני אביב (מאמר נוכחי) • שפת שאילתת REST עם נתוני אביב של JPA ו- Querydsl

• שפת שאילתת REST - פעולות חיפוש מתקדמות

• שפת שאילתת REST - יישום או פעולה

• שפת שאילתת מנוחה עם RSQL

• שפת שאילתת מנוחה עם תמיכה ברשת Querydsl

1. סקירה כללית

במדריך זה - נבנה א API / חיפוש REST לחיפוש / סינון באמצעות JPA JPA ומפרטים.

התחלנו לבחון שפת שאילתות במאמר הראשון בסדרה זו - עם פתרון מבוסס JPA Criteria.

כך - למה שפת שאילתות? כי - לכל ממשק API מורכב מספיק - חיפוש / סינון המשאבים שלך לפי שדות פשוטים מאוד פשוט לא מספיק. שפת שאילתות גמישה יותר ומאפשר לך לסנן בדיוק את המשאבים שאתה צריך.

2. מִשׁתַמֵשׁ יֵשׁוּת

ראשית - נתחיל בפשטות מִשׁתַמֵשׁ ישות לממשק ה- API שלנו לחיפוש:

משתמש בכיתה ציבורית @Entity {@Id @GeneratedValue (אסטרטגיה = GenerationType.AUTO) פרטי מזהה ארוך; פרטי מחרוזת firstName; שם משפחה פרטי מחרוזת; דוא"ל מחרוזת פרטי; גיל פרטי פרטי; // סטרים וקובעים סטנדרטיים}

3. סינון באמצעות מִפרָט

עכשיו - בואו ניגש ישר לחלק המעניין ביותר של הבעיה - שאילתות באמצעות JPA של Spring Data בהתאמה אישית מפרטים.

ניצור a מפרט משתמש המיישם את מִפרָט ממשק ואנחנו הולכים העבירו את האילוץ שלנו לבנות את השאילתה בפועל:

מעמד UserSpecification בכיתה ציבורית מיישם מפרט {קריטריונים של SearchCriteria פרטיים; @Override ציבורי פרדיקט ל- Predicate (שורש שורש, שאילתת CriteriaQuery, בונה CriteriaBuilder) {if (criteria.getOperation (). EqualsIgnoreCase (">")) {return builder.greaterThanOrEqualTo (root. Get (criteria.getKey ()), קריטריונים. getValue (). toString ()); } אחר אם (criteria.getOperation (). equalsIgnoreCase ("<")) {return builder.lessThanOrEqualTo (root. get (criteria.getKey ()), criteria.getValue (). toString ()); } אחר אם (criteria.getOperation (). equalsIgnoreCase (":")) {if (root.get (criteria.getKey ()). getJavaType () == String.class) {return builder.like (root.get ( criteria.getKey ()), "%" + criteria.getValue () + "%"); } אחר {return builder.equal (root.get (criteria.getKey ()), criteria.getValue ()); }} להחזיר null; }}

כמו שאנו יכולים לראות - אנו יוצרים a מִפרָט מבוסס על כמה אילוצים פשוטים שאנו מייצגים בהמשך "SearchCriteria" מעמד:

מחלקה ציבורית SearchCriteria {מפתח מחרוזת פרטי; פעולת מחרוזת פרטית; ערך אובייקט פרטי; }

ה SearchCriteria היישום מחזיק בייצוג בסיסי של אילוץ - והוא מבוסס על אילוץ זה שאנחנו הולכים לבנות את השאילתה:

  • מַפְתֵחַ: שם השדה - למשל, שם פרטי, גיל, … וכו.
  • מבצע: הפעולה - למשל, שוויון, פחות מ ... וכו '.
  • ערך: ערך השדה - למשל, john, 25, ... וכו '.

כמובן שהיישום הוא פשטני וניתן לשפר אותו; אולם זהו בסיס איתן לפעולות חזקות וגמישות שאנו זקוקים לה.

4. ה UserRepository

הבא - בואו נסתכל על ה- UserRepository; אנחנו פשוט מרחיבים את JpaSpecificationExecutor להשיג את ממשקי ה- API החדשים של המפרט:

ממשק ציבורי UserRepository מרחיב את JpaRepository, JpaSpecificationExecutor {}

5. בדוק את שאילתות החיפוש

עכשיו - בואו נבדוק את ה- API החדש לחיפוש.

ראשית, בואו ניצור כמה משתמשים שיהיו מוכנים כאשר הבדיקות פועלות:

@RunWith (SpringJUnit4ClassRunner.class) @ContextConfiguration (classes = {PersistenceJPAConfig.class}) @ Transactional @ TransactionConfiguration public class JPASpecificationsTest {@ מאגר פרטי UserRepository פרטי; משתמש פרטי משתמש ג'ון; UserTom פרטי; @ לפני init בטל פומבי () {userJohn = משתמש חדש (); userJohn.setFirstName ("ג'ון"); userJohn.setLastName ("איילה"); userJohn.setEmail ("[דוא"ל מוגן]"); userJohn.setAge (22); repository.save (userJohn); userTom = משתמש חדש (); userTom.setFirstName ("טום"); userTom.setLastName ("איילה"); userTom.setEmail ("[דוא"ל מוגן]"); userTom.setAge (26); repository.save (userTom); }}

לאחר מכן, בואו נראה כיצד למצוא משתמשים איתם ניתן שם משפחה:

@ מבחן בטל פומבי נתון Last_whenGettingListOfUsers_thenCorrect () {UserSpecification spec = New UserSpecification (new SearchCriteria ("lastName", ":", "doe")); תוצאות רשימה = repository.findAll (spec); assertThat (userJohn, isIn (תוצאות)); assertThat (userTom, isIn (תוצאות)); }

עכשיו, בואו נראה איך למצוא משתמש עם נתון גם שם פרטי וגם שם משפחה:

@Test ציבורי בטל givenFirstAndLastName_whenGettingListOfUsers_thenCorrect () {UserSpecification spec1 = UserSpecification חדש (SearchCriteria חדש ("firstName", ":", "john")); UserSpecification spec2 = UserSpecification חדש (SearchCriteria חדשה ("lastName", ":", "doe")); תוצאות רשימה = repository.findAll (Specification.where (spec1) .and (spec2)); assertThat (userJohn, isIn (תוצאות)); assertThat (userTom, not (isIn (results))); }

הערה: השתמשנו ב- “איפה"ו"ו" ל לשלב מפרט.

לאחר מכן, בואו נראה כיצד למצוא משתמש עם נתון גם שם משפחה וגם גיל מינימום:

@ מבחן פומבי בטל שניתן LastAndAge_whenGettingListOfUsers_thenCorrect () {UserSpecification spec1 = UserSpecification חדש (SearchCriteria חדש ("age", ">", "25")); UserSpecification spec2 = UserSpecification חדש (SearchCriteria חדש ("lastName", ":", "doe")); תוצאות רשימה = repository.findAll (Specification.where (spec1) .and (spec2)); assertThat (userTom, isIn (תוצאות)); assertThat (userJohn, not (isIn (results))); }

עכשיו, בואו נראה איך לחפש מִשׁתַמֵשׁ זֶה לא ממש קיים:

@Test ציבורי בטל givenWrongFirstAndLast_whenGettingListOfUsers_thenCorrect () {UserSpecification spec1 = UserSpecification חדש (SearchCriteria חדש ("firstName", ":", "Adam")); UserSpecification spec2 = UserSpecification חדש (SearchCriteria חדשה ("lastName", ":", "Fox")); תוצאות רשימה = repository.findAll (Specification.where (spec1) .and (spec2)); assertThat (userJohn, not (isIn (results))); assertThat (userTom, not (isIn (results))); }

לסיום - בואו נראה איך למצוא א מִשׁתַמֵשׁ ניתן רק חלק מהשם הפרטי:

@ מבחן חלל ציבורי givenPartialFirst_whenGettingListOfUsers_thenCorrect () {UserSpecification spec = UserSpecification new (SearchCriteria new ("firstName", ":", "jo")); תוצאות רשימה = repository.findAll (spec); assertThat (userJohn, isIn (תוצאות)); assertThat (userTom, not (isIn (results))); }

6. שלבו מפרטים

לאחר מכן - בואו נסתכל על שילוב המנהג שלנו מפרטים להשתמש באילוצים מרובים ולסנן לפי מספר קריטריונים.

אנחנו הולכים ליישם בונה - UserSpecificationsBuilder - לשלב בקלות ובשטף מפרטים:

מחלקה ציבורית UserSpecificationsBuilder {params final list רשימה; UserSpecificationsBuilder ציבורי () {params = ArrayList חדש (); } UserSpecificationsBuilder ציבורי עם (מפתח מחרוזת, פעולת מחרוזת, ערך אובייקט) {params.add (SearchCriteria חדש (מפתח, פעולה, ערך)); להחזיר את זה; } מבנה מפרט ציבורי () {if (params.size () == 0) {return null; } רשימת מפרט = params.stream () .map (UserSpecification :: new) .collect (Collectors.toList ()); תוצאת מפרט = specs.get (0); עבור (int i = 1; i <params.size (); i ++) {result = params.get (i) .isOrPredicate ()? Specification.where (תוצאה). או (specs.get (i)): Specification.where (תוצאה). ו- (specs.get (i)); } להחזיר תוצאה; }}

7. UserController

לסיום - בואו נשתמש בפונקציונליות חדשה זו של חיפוש התמדה / פילטר ו הגדר את ה- REST API - על ידי יצירת a UserController עם פשוט לחפש מבצע:

@Controller מחלקה ציבורית UserController {@ רישום UserRepository פרטי; @RequestMapping (method = RequestMethod.GET, value = "/ users") @ResponseBody public search list (@RequestParam (value = "search") חיפוש מחרוזות) {UserSpecificationsBuilder builder = UserSpecificationsBuilder new (); תבנית תבנית = תבנית. Compile ("(\ w +?) (: |) (\ w +?),"); התאמת התאמה = דפוס. התאמה (חיפוש + ","); בעוד (matcher.find ()) {builder.with (matcher.group (1), matcher.group (2), matcher.group (3)); } מפרט מפרט = builder.build (); החזר repo.findAll (spec); }}

שים לב שכדי לתמוך במערכות אחרות שאינן אנגליות, תבנית ניתן לשנות אובייקט כמו:

תבנית תבנית = תבנית.קומפילציה ("(\ w +?) (: |) (\ w +?),", תבנית.UNICODE_CHARACTER_CLASS);

הנה דוגמה לכתובת URL לבדיקה כדי לבדוק את ה- API:

// localhost: 8080 / משתמשים? search = lastName: איילה, גיל> 25

והתגובה:

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

מכיוון שהחיפושים מפוצלים על ידי "," שלנו תבנית לדוגמא, מונחי החיפוש אינם יכולים להכיל תו זה. הדפוס גם אינו תואם את החלל הלבן.

אם אנו רוצים לחפש ערכים המכילים פסיקים, אנו יכולים לשקול להשתמש במפריד אחר כגון ";".

אפשרות אחרת תהיה לשנות את התבנית לחיפוש ערכים בין המרכאות, ואז להסיר אותם ממונח החיפוש:

תבנית תבנית = תבנית. Compile ("(\ w +?) (: |) (\" ([^ \ "] +) \") ");

8. מסקנה

מדריך זה כיסה יישום פשוט שיכול להוות בסיס לשפת שאילתות חזקה של REST. השתמשנו היטב במפרטי נתוני האביב כדי לוודא שאנחנו מרחיקים את ה- API מהדומיין יש אפשרות לטפל בסוגים רבים אחרים של פעולות.

ה יישום מלא של מאמר זה ניתן למצוא בפרויקט GitHub - זהו פרויקט מבוסס Maven, כך שיהיה קל לייבא ולהפעיל אותו כפי שהוא.

הַבָּא » שפת שאילתת REST עם נתוני אביב JPA ו- Querydsl « שפת שאילתת REST קודמת עם קריטריוני אביב ו- JPA

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