יחסים רבים-רבים ב- JPA

1. הקדמה

במדריך זה נראה מספר דרכים להתמודד עם מערכות יחסים רבות-רבות באמצעות JPA.

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

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

2. בסיסי רבים-רבים

2.1. דוגמנות מערכת יחסים רבים-רבים

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

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

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

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

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

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

2.2. יישום ב- JPA

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

לאחר מכן, עלינו לסמן את הכיתה @יֵשׁוּת, והמפתח הראשי עם @תְעוּדַת זֶהוּת להפוך אותם לגופי JPA נאותים.

כמו כן, עלינו להגדיר את סוג הקשר. לָכֵן אנו מסמנים את האוספים באמצעות @ManyToMany ביאורים:

@ כיתת סטודנט {@Id מזהה ארוך; סט @ManyToMany אהב את הקורסים; // מאפיינים נוספים // קונסטרוקטורים, גטרים וקובעים סטנדרטיים} קורס קורס בכיתה @ @ id ארוך; @ManyToMany סט לייקים; // מאפיינים נוספים // קונסטרוקטורים, גטרים וקובעים סטנדרטיים}

בנוסף, עלינו להגדיר כיצד לדגם את מערכת היחסים ב- RDBMS.

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

אנחנו יכולים לעשות זאת עם @JoinTable ביאור ב סטוּדֶנט מעמד. אנו מספקים את שם טבלת ההצטרפות (כמובן_דומה), והמפתחות הזרים עם @JoinColumn ביאורים. ה להצטרף לעמוד התכונה תתחבר לצד הבעלים של מערכת היחסים, ו- inverseJoinColumn לצד השני:

@ManyToMany @JoinTable (name = "course_like", joinColumns = @JoinColumn (name = "student_id"), inverseJoinColumn = @JoinColumn (name = "course_id")) הגדר קורסים שכבו;

שים לב, כי באמצעות @JoinTable, או אפילו @JoinColumn אינו נדרש: JPA ייצור עבורנו את שמות הטבלאות והעמודות. עם זאת, האסטרטגיה בה JPA משתמשת לא תמיד תואמת את מוסכמות השמות בהן אנו משתמשים. מכאן האפשרות להגדיר את שמות הטבלאות והעמודות.

בצד היעד, עלינו לספק רק את שם השדה, הממפה את הקשר. לכן, הגדרנו את ממופה על ידי תכונה של @ManyToMany ביאור ב קוּרס מעמד:

@ManyToMany (mappedBy = "likedCourses") הגדר לייקים;

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

3. רבים-רבים באמצעות מפתח מורכב

3.1. דוגמנות תכונות יחסים

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

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

זה מצב כאשר למערכת היחסים עצמה יש תכונה.

באמצעות דוגמה זו, צירוף תכונה ליחס נראה כך בתרשים ER:

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

3.2. יצירת מפתח מורכב ב- JPA

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

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

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

@ כיתה Embeddable CourseRatingKey מיישמת באמצעות Serializable {@Column (name = "student_id") סטודנט ארוך; @Column (name = "course_id") קורס קורס ארוך; // בונים סטנדרטיים, גטרים וקובעים // hashcode ושווה ליישום}

שימו לב שיש כאלה דרישות מפתח אשר מחלקת מפתח מורכבת צריכה למלא:

  • עלינו לסמן את זה עם @ ניתן להטמיעה
  • זה צריך ליישם java.io ניתן להתבצע באמצעות סריאליזציה
  • עלינו לספק יישום של ה- hashcode () ו שווים() שיטות
  • אף אחד מהשדות אינו יכול להיות ישות בעצמם

3.3. שימוש במפתח מורכב ב- JPA

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

@ בכיתה קורס קורס דירוג {@ EmbeddedId מזהה קורס דירוג מפתח; @ManyToOne @ MapSid ("studentId") @JoinColumn (name = "student_id") סטודנט סטודנט; @ManyToOne @ MapsId ("courseId") @JoinColumn (name = "course_id") קורס קורס; דירוג int; // בונים סטנדרטיים, גטרים וקובעים}

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

  • השתמשנו @ EmbeddedId, לסימון המפתח הראשי, שהוא מופע של CourseRatingKey מעמד
  • סימנו את סטוּדֶנט ו קוּרס שדות עם @MapsId

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

לאחר מכן, אנו יכולים להגדיר את ההפניות ההפוכות ב- סטוּדֶנט ו קוּרס ישויות כמו בעבר:

תלמיד בכיתה {// ... @OneToMany (mappedBy = "סטודנט") הגדר דירוגים; // ...} קורס בכיתה {// ... @OneToMany (mappedBy = "קורס") הגדר דירוגים; // ...}

שים לב, שיש דרך חלופית להשתמש במקשים מורכבים: ה- @IdClass ביאור.

3.4. מאפיינים נוספים

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

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

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

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

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

4. רבים-רבים עם ישות חדשה

4.1. דוגמנות תכונות יחסים

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

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

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

לָכֵן, אנחנו יכולים להציג ישות, אשר יחזיק בתכונות הרישום:

במקרה הזה, ישות הרישום מייצגת את הקשר בין שתי הישויות האחרות.

מכיוון שמדובר ביישות, יהיה לה מפתח ראשי משלה.

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

4.2. יישום ב- JPA

מאז coure_registration הפך לטבלה רגילה, אנו יכולים ליצור ישות JPA ישנה רגילה המודל אותה:

@ כיתת כניסה לקורס הרשמה {@ איד ארוך מזהה; @ManyToOne @JoinColumn (name = "student_id") סטודנט סטודנט; @ManyToOne @JoinColumn (name = "course_id") קורס קורס; LocalDateTime registeredAt; כיתה int; // מאפיינים נוספים // קונסטרוקטורים, גטרים וקובעים סטנדרטיים}

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

תלמיד בכיתה {// ... @OneToMany (mappedBy = "סטודנט") הגדר רישומים; // ...} קורס בכיתה {// ... @OneToMany (mappedBy = "קורסים") הגדר רישומים; // ...}

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

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

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

5. מסקנה

במדריך זה ראינו מהו מערכת יחסים רבים-רבים וכיצד נוכל לדגם אותה ב- RDBMS באמצעות JPA.

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

  • בהירות קוד
  • בהירות DB
  • יכולת להקצות תכונות למערכת היחסים
  • כמה ישויות נוכל לקשר עם מערכת היחסים, ו
  • תמיכה במספר חיבורים בין אותן ישויות

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