מבוא ל- Querydsl

1. הקדמה

זהו מאמר היכרות שיסייע לכם להתחיל לעבוד עם ממשק ה- API החזק של Querydsl להתמדה בנתונים.

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

2. מטרת Querydsl

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

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

אחת ממסגרות Java ORM הנפוצות ביותר, Hibernate (וגם תקן JPA קשור קשר הדוק), מציעה שפת שאילתות מבוססת מחרוזת HQL (JPQL) הדומה מאוד ל- SQL. החסרונות הברורים בגישה זו הם היעדר בטיחות סוג והיעדר בדיקת שאילתות סטטיות. כמו כן, במקרים מורכבים יותר (למשל, כאשר יש צורך לבנות את השאילתה בזמן ריצה בהתאם לתנאים מסוימים), בניית שאילתת HQL כוללת בדרך כלל שרשור של מחרוזות שבדרך כלל מאוד לא בטוחות ונוטות לשגיאות.

תקן JPA 2.0 הביא לשיפור בצורת ה- Criteria Query API - שיטה חדשה ובטוחה מסוג לבניית שאילתות שניצלה את שיעורי המטא-מודל שנוצרו במהלך עיבוד מקדים של הערות. לרוע המזל, בהיותו פורץ דרך במהותו, ממשק ה- API של Criteria Query הסתיים מאוד מילולית ובלתי קריא. הנה דוגמה מהדרכה של ג'קרטה EE ליצירת שאילתה פשוטה כמו בחר p מתוך Pet p:

EntityManager em = ...; CriteriaBuilder cb = em.getCriteriaBuilder (); CriteriaQuery cq = cb.createQuery (Pet.class); חיית מחמד שורש = cq.from (Pet.class); cq.select (חיית מחמד); TypedQuery q = em.createQuery (cq); רשימת allPets = q.getResultList ();

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

3. דור מחלקות Querydsl

נתחיל ביצירה וחקירה של משקפי המטה הקסומים המהווים את ה- API השוטף של Querydsl.

3.1. הוספת Querydsl ל- Maven Build

הכללת Querydsl בפרויקט שלך היא פשוטה כמו להוסיף מספר תלות בקובץ ה- build שלך ולהגדיר תוסף לעיבוד הערות JPA. נתחיל מהתלות. יש לחלץ את הגרסה של ספריות Querydsl למאפיין נפרד בתוך ה- סעיף, כדלקמן (עבור הגרסה האחרונה של ספריות Querydsl, עיין במאגר Maven Central):

 4.1.3 

לאחר מכן, הוסף את התלות הבאות ל- החלק שלך pom.xml קוֹבֶץ:

  com.querydsl querydsl-apt $ {querydsl.version} סיפק com.querydsl querydsl-jpa $ {querydsl.version} 

ה querydsl-apt תלות היא כלי לעיבוד הערות (APT) - יישום של API API מקביל המאפשר עיבוד של הערות בקבצי מקור לפני שהם עוברים לשלב ההידור. כלי זה מייצר את מה שמכונה סוגי Q- מחלקות המתייחסים ישירות למחלקות היישויות של היישום שלך, אך הם קודמים לאות Q. למשל, אם יש לך מִשׁתַמֵשׁ כיתה מסומנת עם @יֵשׁוּת הערה ביישום שלך, ואז סוג ה- Q שנוצר ישכון ב- QUser.java קובץ מקור.

ה בתנאי היקף ה querydsl-apt תלות פירושה שצנצנת זו צריכה להיות זמינה רק בזמן הבנייה, אך לא להיכלל בחפץ היישום.

ספריית querydsl-jpa היא Querydsl עצמה, שתוכננה לשימוש יחד עם יישום JPA.

כדי להגדיר תוסף לעיבוד הערות המנצל querydsl-apt, הוסף את תצורת התוסף הבאה לפום שלך - בתוך ה- אֵלֵמֶנט:

 com.mysema.maven apt-maven-plugin 1.1.3 יעד תהליך / מקורות שנוצרו / java com.querydsl.apt.jpa.JPAAnnotationProcessor 

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

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

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

משתמש בכיתה ציבורית @Entity {@Id @GeneratedValue פרטי מזהה ארוך; כניסה פרטית למחרוזת; נכים בוליאניים פרטיים; @OneToMany (cascade = CascadeType.PERSIST, mappedBy = "user") פרטית הגדר blogPosts = HashSet חדש (0); // getters and setters} @Post בכיתה הציבורית BlogPost {@Id @GeneratedValue פרטי מזהה ארוך; כותרת מחרוזת פרטית; גוף מחרוזת פרטי; משתמש משתמש פרטי @ManyToOne; // גטרים וקובעים}

כדי ליצור סוגי Q עבור המודל שלך, פשוט הפעל:

לקמפל mvn

3.2. חקר שיעורים שנוצרו

עכשיו עבור לספרייה שצוינה ב- ספריית פלט המאפיין של apt-maven-plugin (יעד / מקורות שנוצרו / ג'אווה בדוגמה שלנו). תראה חבילה ומבנה כיתות המשקפים ישירות את דגם התחום שלך, למעט כל הכיתות מתחילות באות Q (QUser ו QBlogPost במקרה שלנו).

פתח את הקובץ QUser.java. זוהי נקודת הכניסה שלך לבניית כל השאילתות שיש מִשׁתַמֵשׁ כישות שורש. הדבר הראשון שתבחין בו הוא @Generated הערה שמשמעותה שקובץ זה נוצר באופן אוטומטי ואין לערוך אותו ידנית. אם תשנה אחת משיעורי מודל התחום שלך, תצטרך לרוץ לקמפל mvn שוב כדי ליצור מחדש את כל סוגי ה- Q המתאימים.

מלבד כמה QUser בנאים שנמצאים בקובץ זה, עליכם לשים לב גם למופע סופי סטטי ציבורי של QUser מעמד:

משתמש סופי ציבורי סטטי ציבורי = משתמש חדש QUser ("משתמש");

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

הדבר האחרון שצריך לציין הוא שלכל תחום של מחלקת הישויות יש התאמה *נָתִיב שדה מסוג Q, כמו מזהה מספר נתיב, כניסה StringPath ו SetPath blogPosts בתוך ה QUser מחלקה (שימו לב כי שם השדה המתאים ל מַעֲרֶכֶת הוא פלורליזציה). שדות אלה משמשים כחלקים מממשק ה- API השאילתי הרהוט שנפגוש בהמשך.

4. שאילתות עם Querydsl

4.1. שאילתות וסינון פשוטים

כדי לבנות שאילתה, ראשית נצטרך מופע של JPAQueryFactory, שהיא דרך מועדפת להתחיל בתהליך הבנייה. הדבר היחיד ש JPAQueryFactory צרכים זה EntityManager, שכבר אמור להיות זמין ביישום JPA שלך דרך EntityManagerFactory.createEntityManager () להתקשר או @PersistenceContext זריקה.

EntityManagerFactory emf = Persistence.createEntityManagerFactory ("com.baeldung.querydsl.intro"); EntityManager em = entityManagerFactory.createEntityManager (); JPAQueryFactory queryFactory = JPAQueryFactory חדש (em);

עכשיו בואו ניצור את השאילתה הראשונה שלנו:

משתמש QUser = QUser.user; משתמש c = queryFactory.selectFrom (user) .where (user.login.eq ("David")) .fetchOne ();

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

ה לבחור מ שיטת ה- JPAQueryFactory מתחיל לבנות שאילתה. אנחנו מעבירים את זה QUser למשל ולהמשיך לבנות את הסעיף המותנה של השאילתה באמצעות ה- .איפה() שיטה. ה כניסת משתמש הוא התייחסות לא StringPath שדה של QUser שיעור שראינו בעבר. ה StringPath לאובייקט יש גם את .eq () שיטה המאפשרת להמשיך בבניית השאילתה באופן שוטף על ידי ציון תנאי השוויון בשדה.

לבסוף, כדי להביא את הערך ממסד הנתונים להקשר התמדה, אנו מסיימים את שרשרת הבניין עם הקריאה ל- fetchOne () שיטה. שיטה זו חוזרת ריק אם האובייקט לא נמצא, אבל זורק א NonUniqueResultException אם יש מספר ישויות המספקות את .איפה() מַצָב.

4.2. הזמנה וקיבוץ

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

רשימה c = queryFactory.selectFrom (user) .orderBy (user.login.asc ()) .fetch ();

תחביר זה אפשרי מכיוון שה- *נָתִיב בשיעורים יש את .asc () ו .desc () שיטות. אתה יכול גם לציין מספר ארגומנטים עבור ה- .מיין לפי() שיטה למיין לפי מספר שדות.

עכשיו בואו ננסה משהו יותר קשה. נניח שעלינו לקבץ את כל ההודעות לפי כותרת ולספור כותרות משוכפלות. זה נעשה עם .groupBy () סָעִיף. נרצה להזמין את הכותרות לפי ספירת התרחשויות כתוצאה מכך.

NumberPath count = Expressions.numberPath (Long.class, "c"); רשום userTitleCounts = queryFactory.select (blogPost.title, blogPost.id.count (). As (count)). From (blogPost) .groupBy (blogPost.title) .orderBy (count.desc ()) .fetch ();

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

4.3. שאילתות מורכבות עם צירופים ושאילתות משנה

בואו נמצא את כל המשתמשים שכתבו פוסט שכותרתו "שלום עולם!" לשאילתה כזו נוכל להשתמש בחיבור פנימי. שימו לב שיצרנו כינוי פוסט בבלוג עבור השולחן המצורף להתייחס אליו ב- .עַל() סָעִיף:

QBlogPost blogPost = QBlogPost.blogPost; רשימת משתמשים = queryFactory.selectFrom (user) .innerJoin (user.blogPosts, blogPost). On (blogPost.title.eq ("שלום עולם!")) .Fetch ();

עכשיו בואו ננסה להשיג את אותו הדבר באמצעות שאילתת משנה:

רשימת משתמשים = queryFactory.selectFrom (user) .where (user.id.in (JPAExpressions.select (blogPost.user.id). From (blogPost) .where (blogPost.title.eq ("שלום עולם!"))) אחזר ();

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

4.4. שינוי נתונים

JPAQueryFactory מאפשר לא רק לבנות שאילתות, אלא גם לשנות ולמחוק רשומות. בואו נשנה את הכניסה של המשתמש ונשבית את החשבון:

queryFactory.update (user) .where (user.login.eq ("Ash")) .set (user.login, "Ash2") .set (user.disabled, true) .execute ();

אנחנו יכולים לקבל כל מספר של .מַעֲרֶכֶת() סעיפים שאנחנו רוצים לתחומים שונים. ה .איפה() סעיף אינו הכרחי, כך שנוכל לעדכן את כל הרשומות בבת אחת.

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

queryFactory.delete (user) .where (user.login.eq ("David")) .execute ();

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

אתה יכול לתהות, מדוע JPAQueryFactory אין את .לְהַכנִיס() שיטה. זו מגבלה על ממשק שאילתת JPA. הבסיסי javax.persistence.Query.executeUpdate () השיטה מסוגלת לבצע עדכון ומחיקה אך לא להוסיף הצהרות. כדי להוסיף נתונים, עליך פשוט להתמיד את הישויות באמצעות EntityManager.

אם אתה עדיין רוצה לנצל תחביר דומה של Querydsl להכנסת נתונים, עליך להשתמש SQLQueryFactory מחלקה השוכנת בספריית querydsl-sql.

5. מסקנה

במאמר זה גילינו ממשק API חזק ובטיחותי למניפולציה של אובייקטים מתמשכים שמספק Querydsl.

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

כל קוד המקור של הדוגמאות נמצא במאגר github.

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