הקשר התמדה של JPA / Hibernate

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

ספקי התמדה כמו Hibernate משתמשים בהקשר ההתמדה לניהול מחזור החיים של היישות ביישום.

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

2. הקשר התמדה

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

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

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

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

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

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

3. סוג ההקשר להתמדה

הקשרים של התמדה קיימים בשני סוגים:

  • הקשר התמדה בהיקף העסקה
  • הקשר התמדה מורחב

בואו נסתכל על כל אחד מהם.

3.1 ההקשר להתמדה בהיקף העסקה

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

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

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

@PersistenceContext EntityManager entityManager פרטי;

3.2 הקשר התמדה מורחב

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

לספר EntityManager כדי להשתמש בהקשר ההתמדה המורחב, עלינו להחיל את ה- סוּג תכונה של @PersistenceContext:

@PersistenceContext (type = PersistenceContextType.EXTENDED) EntityManager entityManager פרטי;

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

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

4. דוגמא להקשר התמדה

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

ראשית, בואו ניצור את מחלקת השירות שלנו, TransctionPersistenceContextUserService:

@Component public class TransactionPersistenceContextUserService {@PersistenceContext private EntityManager entityManager; @ Transactional משתמש משתמש insertWithTransaction (משתמש משתמש) {entityManager.persist (משתמש); משתמש חוזר; } משתמש ציבורי insertWithoutTransaction (משתמש משתמש) {entityManager.persist (משתמש); משתמש חוזר; } חיפוש משתמשים ציבורי (מזהה ארוך) {return entityManager.find (User.class, id); }}

השיעור הבא, ExtendedPersistenceContextUserService, דומה מאוד לאמור לעיל, למעט ה- @PersistenceContext ביאור. הפעם אנחנו עוברים PersistenceContextType.EXTENDED לתוך ה סוּג פרמטר שלה @PersistenceContext ביאור:

מחלקה ציבורית @Component ExtendedPersistenceContextUserService {@PersistenceContext (type = PersistenceContextType.EXTENDED) EntityManager entityManager פרטי; // קוד שנותר זהה לעיל}

5. מקרי מבחן

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

5.1 בדיקת הקשר להתמדה בעסקה

בואו נתמיד א מִשׁתַמֵשׁ ישות המשתמשת בהקשר ההתמדה המצומצם. הישות תישמר באחסון מתמשך. לאחר מכן אנו מאמתים על ידי ביצוע שיחת חיפוש באמצעות הקשרים של ההתמדה המורחבת שלנו EntityManager:

משתמש משתמש = משתמש חדש (121L, "Devender", "admin"); transctionPersistenceContext.insertWithTransaction (משתמש); משתמש userFromTransctionPersistenceContext = transctionPersistenceContext .find (user.getId ()); assertNotNull (userFromTransctionPersistenceContext); משתמש userFromExtendedPersistenceContext = ExtendedPersistenceContext .find (user.getId ()); assertNotNull (userFromExtendedPersistenceContext);

כאשר אנו מנסים להכניס א מִשׁתַמֵשׁ ישות ללא עסקה, אם כן TransactionRequiredException ייזרק:

@Test (צפוי = TransactionRequiredException.class) מבט חיסול ציבורי testThatUserSaveWithoutTransactionThrowException () {משתמש משתמש = משתמש חדש (122L, "Devender", "admin"); transctionPersistenceContext.insertWithoutTransaction (משתמש); }

5.2 בדיקת הקשר להתמדה מורחבת

לאחר מכן, בואו נמשך את המשתמש בהקשר התמדה מורחב וללא עסקה. ה מִשׁתַמֵשׁ הישות תישמר בהקשר ההתמדה (מטמון) אך לא באחסון מתמשך:

משתמש משתמש = משתמש חדש (123L, "Devender", "admin"); extendedPersistenceContext.insertWithoutTransaction (משתמש); משתמש userFromExtendedPersistenceContext = ExtendedPersistenceContext .find (user.getId ()); assertNotNull (userFromExtendedPersistenceContext); משתמש userFromTransctionPersistenceContext = transctionPersistenceContext .find (user.getId ()); assertNull (userFromTransctionPersistenceContext);

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

@Test (צפוי = EntityExistsException.class) test public voidThatPersistUserWithSameIdentifierThrowException () {User user1 = משתמש חדש (126L, "Devender", "admin"); משתמש משתמש 2 = משתמש חדש (126L, "Devender", "admin"); extendedPersistenceContext.insertWithoutTransaction (user1); extendedPersistenceContext.insertWithoutTransaction (user2); }

נראה EntityExistsException:

javax.persistence.EntityExistsException: אובייקט אחר עם אותו ערך מזהה כבר היה משויך להפעלה

הקשר התמדה מורחב בתוך עסקה שומר את הישות באחסון מתמשך בסוף העסקה:

משתמש משתמש = משתמש חדש (127L, "Devender", "admin"); extendedPersistenceContext.insertWithTransaction (משתמש); משתמש userFromDB = transctionPersistenceContext.find (user.getId ()); assertNotNull (userFromDB);

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

משתמש משתמש 1 = משתמש חדש (124L, "Devender", "admin"); extendedPersistenceContext.insertWithoutTransaction (user1); משתמש user2 = משתמש חדש (125L, "Devender", "admin"); extendedPersistenceContext.insertWithTransaction (user2); משתמש user1FromTransctionPersistenceContext = transctionPersistenceContext .find (user1.getId ()); assertNotNull (user1FromTransctionPersistenceContext); משתמש user2FromTransctionPersistenceContext = transctionPersistenceContext .find (user2.getId ()); assertNotNull (user2FromTransctionPersistenceContext);

6. מסקנה

במדריך זה קיבלנו הבנה טובה של הקשר ההתמדה.

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

כמו תמיד, קוד הדוגמה זמין ב- GitHub.