אביב נתונים JPA @ Query

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

Spring Data מספק דרכים רבות להגדיר שאילתה שנוכל לבצע. אחד מאלה הוא ה @שאילתא ביאור.

במדריך זה נדגים כיצד להשתמש ב- @שאילתא הערה ב- Spring Data JPA לצורך ביצוע שאילתות JPQL וגם שאילתות SQL מקוריות.

אנו נראה גם כיצד לבנות שאילתה דינמית כאשר ה- @שאילתא ביאור אינו מספיק.

2. בחר שאילתה

על מנת להגדיר את SQL לביצוע עבור שיטת מאגר נתונים של Spring, אנו יכולים ביאור השיטה באמצעות @שאילתא ביאור - שלה ערך תכונה מכילה את ה- JPQL או SQL לביצוע.

ה @שאילתא ההערה קודמת לשאילתות בעלות שם, בהן רשום הערות @NamedQuery או מוגדר ב- orm.xml קוֹבֶץ.

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

2.1. JPQL

כברירת מחדל, הגדרת השאילתה משתמשת ב- JPQL.

בואו נסתכל על שיטת מאגר פשוטה המחזירה פעילה מִשׁתַמֵשׁ ישויות ממסד הנתונים:

@Query ("בחר u ממשתמש u WHERE u.status = 1") אוסף findAllActiveUsers (); 

2.2. יָלִיד

אנו יכולים להשתמש גם ב- SQL מקורי להגדרת השאילתה שלנו. כל שעלינו לעשות הוא קבע את הערך של nativeQuery מייחס ל נָכוֹן ולהגדיר את שאילתת ה- SQL המקורית ב- ערך מאפיין ההערה:

@Query (value = "בחר * ממשתמשים u WHERE u.status = 1", nativeQuery = true) אוסף findAllActiveUsersNative (); 

3. הגדר סדר בשאילתה

אנו יכולים להעביר פרמטר נוסף מסוג סוג להצהרת שיטת Spring Data שיש לה את @שאילתא ביאור. זה יתורגם ל מיין לפי סעיף שמועבר למסד הנתונים.

3.1. מיון לפי שיטות JPA המסופקות ונגזרות

לשיטות שאנחנו יוצאים מהקופסה כמו findAll (מיין) או אלה שנוצרים על ידי חתימות שיטת ניתוח, אנו יכולים להשתמש רק במאפייני אובייקט כדי להגדיר את המיון שלנו:

userRepository.findAll (מיון חדש (Sort.Direction.ASC, "שם")); 

עכשיו דמיין שאנחנו רוצים למיין לפי אורך של מאפיין שם:

userRepository.findAll (מיון חדש ("LENGTH (name)")); 

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

org.springframework.data.mapping.PropertyReferenceException: לא נמצא מאפיין LENGTH (שם) לסוג User!

3.2. JPQL

כאשר אנו משתמשים ב- JPQL להגדרת שאילתה, Spring Data יכולה להתמודד עם מיון ללא כל בעיה - כל שעלינו לעשות הוא להוסיף פרמטר שיטה מסוג סוג:

@Query (value = "בחר u ממשתמש u") רשימה findAllUsers (מיין מיון); 

אנו יכולים לקרוא לשיטה זו ולהעביר א סוג הפרמטר, אשר יזמין את התוצאה על ידי שֵׁם הקניין של מִשׁתַמֵשׁ לְהִתְנַגֵד:

userRepository.findAllUsers (מיון חדש ("שם"));

ומכיוון שהשתמשנו ב @שאילתא ביאור, אנו יכולים להשתמש באותה שיטה כדי לקבל את הרשימה הממוינת של משתמשים לפי אורך שמותיהם:

userRepository.findAllUsers (JpaSort.unsafe ("LENGTH (name)")); 

חשוב לנו להשתמש בו JpaSort.unsafe () ליצור סוג מופע אובייקט.

כאשר אנו משתמשים:

מיון חדש ("LENGTH (name)"); 

אז נקבל בדיוק את אותו החריג כפי שראינו לעיל עבור ה- מצא הכל() שיטה.

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

3.3. יָלִיד

כאשר @שאילתא ההערה משתמשת ב- SQL מקורי, ואז לא ניתן להגדיר a סוג.

אם כן, נקבל חריג:

org.springframework.data.jpa.repository.query.InvalidJpaQueryMethodException: לא ניתן להשתמש בשאילתות מקוריות עם מיון דינמי ו / או עמוד

כפי שאומר החריג, המיון אינו נתמך בשאילתות מקוריות. הודעת השגיאה נותנת לנו רמז לכך שגם עימוד יגרום לחריג.

עם זאת, יש דרך לעקיפת הבעיה המאפשרת עימוד, ואנחנו נסקור אותה בחלק הבא.

4. עימוד

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

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

4.1. JPQL

השימוש בעימוד בהגדרת שאילתת JPQL הוא פשוט:

@Query (value = "בחר u ממשתמש u סדר לפי מזהה") עמוד findAllUsersWithPagination (ניתן לעמוד עם דף); 

אנחנו יכולים להעביר א PageRequest פרמטר כדי לקבל דף נתונים.

עימוד נתמך גם בשאילתות מקוריות אך דורש מעט עבודה נוספת.

4.2. יָלִיד

אנחנו יכולים אפשר העמדה לשאילתות מקוריות על ידי הצהרת תכונה נוספת countQuery.

זה מגדיר את ה- SQL שיש לבצע כדי לספור את מספר השורות בתוצאה כולה:

@Query (value = "SELECT * FROM Users ORDER BY id", countQuery = "SELECT count (*) FROM Users", nativeQuery = true) עמוד findAllUsersWithPagination (ניתן לעמוד דף);

4.3. עדכוני גרסאות JPA לפני 2.0.4

הפתרון שלמעלה לשאילתות מקוריות עובד מצוין עבור Spring Data JPA גרסאות 2.0.4 ואילך.

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

אנו יכולים להתגבר על כך על ידי הוספת פרמטר נוסף לעמוד בתוך השאילתה שלנו:

@Query (value = "בחר * ממשתמשים סדר לפי מזהה \ n-- # דף \ n", countQuery = "בחר ספירה (*) מהמשתמשים", nativeQuery = נכון) דף findAllUsersWithPagination (דף ניתן לעמוד);

בדוגמה שלעיל, אנו מוסיפים "\ n– #pageable \ n" כמציין המיקום של פרמטר העמודים. זה אומר ל- Spring Data JPA כיצד לנתח את השאילתה ולהזריק את הפרמטר הניתן לעמוד. פתרון זה עובד עבור H2 מאגר מידע.

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

5. פרמטרים של שאילתות באינדקס

ישנן שתי דרכים אפשריות בהן אנו יכולים להעביר פרמטרים לשיטה לשאילתה שלנו: פרמטרים באינדקס ושמות.

בחלק זה נסקור פרמטרים שנוספו לאינדקס.

5.1. JPQL

עבור פרמטרים באינדקס ב- JPQL, Spring Data יעשה זאת העבירו פרמטרים לשיטה לשאילתה באותו סדר שהם מופיעים בהצהרת השיטה:

@Query ("בחר u ממשתמש u WHERE u.status =? 1") המשתמש findUserByStatus (מצב שלם); @Query ("בחר U ממשתמש u WHERE u.status =? 1 ו- u.name =? 2") המשתמש findUserByStatusAndName (מצב שלם, שם מחרוזת); 

לשאילתות הנ"ל, סטָטוּס פרמטר השיטה יוקצה לפרמטר השאילתה עם האינדקס 1, וה שֵׁם פרמטר השיטה יוקצה לפרמטר השאילתה עם האינדקס 2.

5.2. יָלִיד

פרמטרים באינדקס עבור השאילתות המקוריות פועלים בדיוק באותו אופן כמו עבור JPQL:

@Query (value = "בחר * ממשתמשים u WHERE u.status =? 1", nativeQuery = true) המשתמש findUserByStatusNative (מצב שלם);

בחלק הבא נציג גישה שונה: העברת פרמטרים דרך שם.

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

אנחנו יכולים גם העבר פרמטרים לשיטה לשאילתה באמצעות פרמטרים בשם. אנו מגדירים אותם באמצעות ה- @ פארם ביאור בתוך הצהרת שיטת המאגר שלנו.

כל פרמטר רשום עם @ פארם חייב להיות מחרוזת ערך שתואמת את שם הפרמטר המקביל לשאילתת JPQL או SQL. קל יותר לקריאה של שאילתה עם פרמטרים עם שם והיא פחות נוטה לשגיאות במקרה שיש לשחזר את השאילתה.

6.1. JPQL

כאמור לעיל, אנו משתמשים ב- @ פארם ביאור בהצהרת השיטה כדי להתאים פרמטרים המוגדרים לפי שם ב- JPQL לפרמטרים מהצהרת השיטה:

@Query ("בחר אותך ממשתמש u WHERE u.status =: status ו- u.name =: name") משתמש findUserByStatusAndNameNamedParams (@Param ("סטטוס") מצב שלם, @Param ("שם") שם מחרוזת); 

שים לב שבדוגמה שלמעלה הגדרנו את שאילתות ה- SQL והפרמטרים של השיטה שלהם לשם זהה, אך זה לא נדרש כל עוד מחרוזות הערך זהות:

@Query ("בחר u ממשתמש u WHERE u.status =: סטטוס ו- u.name =: שם") משתמש findUserByUserStatusAndUserName (@Param ("סטטוס") משתמש שלם UserStatus, @Param ("שם") שם משתמש מחרוזת); 

6.2. יָלִיד

להגדרת השאילתה המקורית, אין הבדל באופן העברת פרמטר דרך השם לשאילתה בהשוואה ל- JPQL - אנו משתמשים @ פארם ביאור:

@Query (value = "בחר * ממשתמשים u WHERE u.status =: status and u.name =: name", nativeQuery = true) user findUserByStatusAndNameNamedParamsNative (@Param ("status") Status integral, @Param ("name" ) שם מחרוזת);

7. פרמטר אוסף

בואו ניקח בחשבון את המקרה כאשר איפה סעיף של שאילתת JPQL או SQL שלנו מכיל את IN (אוֹ לא במילת מפתח:

בחר u ממשתמש u היכן u.name IN: שמות

במקרה זה, אנו יכולים להגדיר שיטת שאילתה הנדרשת אוסף כפרמטר:

@Query (value = "בחר u ממשתמש u WHERE u.name IN: names") רשימה findUserByNameList (@Param ("names") שמות אוספים);

כפרמטר הוא א אוסף, ניתן להשתמש בו עם רשימה, HashSet, וכו.

לאחר מכן, נראה כיצד לשנות נתונים באמצעות ה- @משנה ביאור.

8. עדכן שאילתות עם @ שינוי

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

8.1. JPQL

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

@Modifying @Query ("עדכון משתמש אתה מגדיר u.status =: מצב שבו u.name =: שם") int updateUserSetStatusForName (@Param ("status") מצב שלם, @Param ("שם") שם מחרוזת); 

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

8.2. יָלִיד

אנו יכולים לשנות את מצב מסד הנתונים גם בעזרת שאילתה מקורית. אנחנו רק צריכים להוסיף את @ שינוי ביאור:

@Modifying @Query (value = "update users u set u.status =? Where u.name =?", NativeQuery = true) int updateUserSetStatusForNameNative (מצב שלם, שם מחרוזת);

8.3. הוספות

כדי לבצע פעולת הכנסה, עלינו להחיל את שניהם @ שינוי והשתמש בשאילתת מקור שכן INSERT אינו חלק מממשק JPA:

@Modifying @Query (value = "הכנס למשתמשים (שם, גיל, דוא"ל, סטטוס) ערכים (: שם,: גיל,: דוא"ל,: סטטוס)", nativeQuery = true) ריק insertUser (@Param ("שם")) שם מחרוזת, @Param ("גיל") גיל שלם, @Param ("סטטוס") סטטוס שלם, @Param ("דוא"ל") דוא"ל מחרוזת);

9. שאילתה דינמית

לעיתים קרובות ניתקל בצורך בבניית הצהרות SQL על סמך תנאים או מערכי נתונים שערכיהם ידועים רק בזמן הריצה. ובמקרים אלה, איננו יכולים להשתמש בשאילתה סטטית בלבד.

9.1. דוגמה לשאילתה דינמית

לדוגמא, בואו נדמיין מצב בו עלינו לבחור את כל המשתמשים שהדוא"ל שלהם הוא כמו אחד מתוך קבוצה שהוגדרה בזמן הריצה - דוא"ל 1, דוא"ל 2, …, דואר אלקטרוני:

בחר u ממשתמש u WHERE u.email LIKE '% email1%' או u.email LIKE '% email2%' ... או u.email LIKE '% emailn%'

מכיוון שהסט בנוי באופן דינמי, איננו יכולים לדעת בזמן הידור כמה כמו סעיפים להוסיף.

במקרה הזה, אנחנו לא יכולים פשוט להשתמש ב- @שאילתא ביאור מכיוון שאיננו יכולים לספק משפט SQL סטטי.

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

9.2. מאגרים מותאמים אישית ו- API של קריטריונים של JPA

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

נתחיל ביצירת ממשק פרגמנטים מותאם אישית:

ממשק ציבורי UserRepositoryCustom {List findUserByEmails (הגדר אימיילים); }

ואז נבצע את זה:

מחלקה ציבורית UserRepositoryCustomImpl מיישמת UserRepositoryCustom {@PersistenceContext פרטי EntityManager entityManager; @ רישום ציבורי @ FindUserByEmails (הגדר אימיילים) {CriteriaBuilder cb = entityManager.getCriteriaBuilder (); שאילתת CriteriaQuery = cb.createQuery (User.class); משתמש שורש = query.from (User.class); נתיב emailPath = user.get ("דוא"ל"); רשימת פרדיקטים = ArrayList חדש (); עבור (מייל מחרוזת: מיילים) {predicates.add (cb.like (emailPath, email)); } query.select (user) .where (cb.or (predicates.toArray (Predicate new [predicates.size ()]))); החזר entityManager.createQuery (שאילתה) .getResultList (); }}

כפי שמוצג לעיל, השתמשנו ב- API של קריטריונים JPA לבניית השאילתה הדינמית שלנו.

כמו כן, עלינו לוודא לכלול את ה- יישום postfix בשם הכיתה. האביב יחפש את UserRepositoryCustom יישום כ UserRepositoryCustomImpl. מאחר ושברי אינם מאגרים בפני עצמם, ספרינג מסתמך על מנגנון זה כדי למצוא את יישום השבר.

9.3. הרחבת המאגר הקיים

שים לב שכל שיטות השאילתה מסעיף 2 עד סעיף 7 נמצאות ב UserRepository.

אז, כעת נשלב את השבר שלנו על ידי הרחבת הממשק החדש ב- UserRepository:

ממשק ציבורי UserRepository מרחיב את שיטות השאילתה JpaRepository, UserRepositoryCustom {// מסעיף 2 - סעיף 7}

9.4. שימוש במאגר

ולבסוף, אנו יכולים לקרוא לשיטת השאילתות הדינמיות שלנו:

הגדר מיילים = HashSet חדש (); // מילוי הסט עם כל מספר פריטים userRepository.findUserByEmails (מיילים); 

יצרנו בהצלחה מאגר מורכב וקראנו לשיטה המותאמת אישית שלנו.

10. מסקנה

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

למדנו גם כיצד ליישם מאגר מותאם אישית וליצור שאילתה דינמית.

כמו תמיד, דוגמאות הקוד המלאות המשמשות במאמר זה זמינות באתר GitHub.