סקירה כללית של סוגי אשדות JPA / Hibernate

1. הקדמה

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

2. מה זה מדורגים?

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

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

2.1. JPA סוג אשד

כל פעולות המפל הספציפיות ל- JPA מיוצגות על ידי javax.persistence.CascadeType enum המכיל ערכים:

  • את כל
  • להתמיד
  • לְמַזֵג
  • לְהַסִיר
  • לְרַעֲנֵן
  • לנתק

2.2. סוג אשד שינה

Hibernate תומך בשלושה סוגי אשדות נוספים יחד עם אלה שצוינו על ידי JPA. סוגי אשדות ספציפיים למצב שינה זה זמינים ב org.hibernate.annotations.CascadeType:

  • שכפול
  • SAVE_UPDATE
  • לנעול

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

3.1. CascadeType.את כל

Cascade.ALLמפיץ את כל הפעולות - כולל פעולות ספציפיות למצב שינה - מהורה לישות ילדית.

בואו נראה את זה בדוגמה:

מזהה ציבורי @Entity {@Id @GeneratedValue (אסטרטגיה = GenerationType.AUTO) מזהה פרטי פרטי; שם מחרוזת פרטי; @OneToMany (mappedBy = "person", cascade = CascadeType.ALL) כתובות רשימה פרטית; }

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

עכשיו, בואו נראה את הישות המשויכת כתובת:

כתובת בכיתה ציבורית @Entity {@Id @GeneratedValue (אסטרטגיה = GenerationType.AUTO) מזהה פרטי פרטי; רחוב מיתרים פרטי; בית פרטי פרטי מספר; עיר מיתרים פרטית; פרטי int zipCode; @ManyToOne (fetch = FetchType.LAZY) אדם פרטי; }

3.2. CascadeType.להתמיד

פעולת ההתמדה הופכת מופע חולף למתמשך. CascadeType להתמיד מפיץ את הפעולה המתמדת מהורה לישות ילדית. כאשר אנו שומרים את אדם הישות, ה כתובת הישות גם תישמר.

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

@ מבחן ציבורי בטל כאשרParentSavedThenChildSaved () {אדם אדם = אדם חדש (); כתובת כתובת = כתובת חדשה (); address.setPerson (אדם); person.setAddresses (Arrays.asList (כתובת)); session.persist (אדם); session.flush (); session.clear (); }

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

מצב שינה: הכנס לערכי אדם (שם, מזהה) (?,?) מצב שינה: הכנס לערכי כתובת (עיר, בית מספר, שם_אדם, רחוב, מיקוד, מיקוד) (?,?,?,?,?,?)

3.3. CascadeType.לְמַזֵג

פעולת המיזוג מעתיקה את מצב האובייקט הנתון לאובייקט המתמיד עם אותו מזהה. CascadeType.MERGE מפיץ את פעולת המיזוג מהורה לישות ילדית.

בואו נבדוק את פעולת המיזוג:

@Test ציבורי בטל whenParentSavedThenMerged () {int addressId; אדם אדם = buildPerson ("devender"); כתובת כתובת = buildAddress (אדם); person.setAddresses (Arrays.asList (כתובת)); session.persist (אדם); session.flush (); addressId = address.getId (); session.clear (); כתובת להציל את הכתובת AdressEntity = session.find (Address.class, addressId); אדם savedPersonEntity = saveAddressEntity.getPerson (); savedPersonEntity.setName ("קומנדר devender"); savedAddressEntity.setHouseNumber (24); session.merge (savedPersonEntity); session.flush (); }

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

תרדמת שינה: בחר כתובת מצב שינה: בחר person0_.id כ- id1_1_0_, person0_.name כשם2_1_0_ מ- person person0_ איפה person0_.id =? מצב שינה: עדכון ערכת הכתובת city = ?, houseNumber = ?, person_id = ?, street = ?, zipCode =? איפה id =? תרדמת שינה: עדכן שם קבוצת אנשים =? איפה id =?

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

3.4. CascadeType.REMOVE

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

CascadeType.REMOVE מפיץ את פעולת ההסרה מישות הורה לילד.דומה לזה של JPA CascadeType.REMOVE, יש לנו CascadeType.DELETE, שהוא ספציפי למצב שינה. אין הבדל בין השניים.

עכשיו הגיע הזמן לבדוק CascadeType. הסר:

@Test הציבור בטל כאשרParentRemovedThenChildRemoved () {int personId; אדם אדם = buildPerson ("devender"); כתובת כתובת = buildAddress (אדם); person.setAddresses (Arrays.asList (כתובת)); session.persist (אדם); session.flush (); personId = person.getId (); session.clear (); אדם הצילPersonEntity = session.find (Person.class, personId); session.remove (savedPersonEntity); session.flush (); }

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

שינה: מחק מכתובת איפה id =? שינה: מחק מאדם איפה id =?

ה כתובת הקשורים ל אדם הוסר גם כתוצאה מ CascadeType הסר.

3.5. CascadeType.DETACH

פעולת הניתוק מסירה את הישות מההקשר המתמשך. כשאנחנו משתמשים CascaseType.DETACH, הישות הילדית תוסר גם מההקשר המתמשך.

בואו נראה את זה בפעולה:

@ מבחן ציבורי בטל כאשרParentDetachedThenChildDetached () {אדם אדם = buildPerson ("devender"); כתובת כתובת = buildAddress (אדם); person.setAddresses (Arrays.asList (כתובת)); session.persist (אדם); session.flush (); assertThat (session.contains (אדם)). isTrue (); assertThat (session.contains (address)). isTrue (); session.detach (אדם); assertThat (session.contains (אדם)). isFalse (); assertThat (session.contains (address)). isFalse (); }

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

3.6. CascadeType.לנעול

באופן לא אינטואיטיבי, CascadeType.LOCK מצרף מחדש את הישות ואת ישות הילד המשויכת לה בהקשר המתמשך.

בואו נראה את מקרה הבדיקה כדי להבין CascadeType.LOCK:

@ מבחן ציבורי בטל כאשרDetachedAndLockedThenBothReaattached () {אדם אדם = buildPerson ("devender"); כתובת כתובת = buildAddress (אדם); person.setAddresses (Arrays.asList (כתובת)); session.persist (אדם); session.flush (); assertThat (session.contains (אדם)). isTrue (); assertThat (session.contains (address)). isTrue (); session.detach (אדם); assertThat (session.contains (אדם)). isFalse (); assertThat (session.contains (address)). isFalse (); session.unwrap (Session.class) .buildLockRequest (LockOptions חדש (LockMode.NONE)) .lock (אדם); assertThat (session.contains (אדם)). isTrue (); assertThat (session.contains (address)). isTrue (); }

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

3.7. CascadeType.לְרַעֲנֵן

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

בתרחיש מסוג זה, זה עשוי להיות שימושי. כאשר אנו משתמשים בפעולה זו עם CascadeType לְרַעֲנֵן, גם ישות הצאצא נטענת מחדש ממסד הנתונים בכל פעם שהיישות האב מתרעננת.

להבנה טובה יותר, בואו נראה מקרה מבחן עבור CascadeType.REFRESH:

@ מבחן ציבורי בטל כאשרParentRefreshedThenChildRefreshed () {אדם אדם = buildPerson ("devender"); כתובת כתובת = buildAddress (אדם); person.setAddresses (Arrays.asList (כתובת)); session.persist (אדם); session.flush (); person.setName ("Devender Kumar"); address.setHouseNumber (24); session.refresh (אדם); assertThat (person.getName ()). isEqualTo ("devender"); assertThat (address.getHouseNumber ()). isEqualTo (23); }

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

3.8. CascadeType.REPLICATE

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

עכשיו, בואו לבדוק CascadeType.שכפול:

@ מבחן ציבורי בטל כאשרParentReplicatedThenChildReplicated () {אדם אדם = buildPerson ("devender"); person.setId (2); כתובת כתובת = buildAddress (אדם); address.setId (2); person.setAddresses (Arrays.asList (כתובת)); session.unwrap (Session.class). replicate (person, ReplicationMode.OVERWRITE); session.flush (); assertThat (person.getId ()). isEqualTo (2); assertThat (address.getId ()). isEqualTo (2); }

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

3.9. CascadeType.SAVE_UPDATE

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

בוא נראה CascadeType.SAVE_UPDATE בִּפְעוּלָה:

@Test ציבורי בטל כאשרParentSavedThenChildSaved () {אדם אדם = buildPerson ("devender"); כתובת כתובת = buildAddress (אדם); person.setAddresses (Arrays.asList (כתובת)); session.saveOrUpdate (אדם); session.flush (); }

בגלל CascadeType.SAVE_UPDATEכאשר אנו מריצים את מקרה הבדיקה הנ"ל, אנו יכולים לראות כי ה- אדם ו כתובת שניהם ניצלו. הנה ה- SQL שנוצר:

מצב שינה: הכנס לערכי אדם (שם, מזהה) (?,?) מצב שינה: הכנס לערכי כתובת (עיר, בית מספר, שם_אדם, רחוב, מיקוד, מיקוד) (?,?,?,?,?,?)

4. מסקנה

במאמר זה דנו במפל ואפשרויות שונות מסוג מפל זמינות ב- JPA ו- Hibernate.

קוד המקור של המאמר זמין ב- GitHub.