מדריך לג'קרטה EE JTA

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

ממשק API של Java Transaction, הידוע יותר בשם JTA, הוא API לניהול עסקאות בג'אווה. זה מאפשר לנו להתחיל, לבצע ולהחזיר עסקאות בצורה אגנוסטית של משאבים.

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

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

2. ממשק API אוניברסלי ועסקאות מבוזרות

JTA מספקת הפשטה על בקרת עסקאות (התחלה, התחייבות והחזרות) לקוד העסקי.

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

לדוגמה, עלינו להתמודד עם משאב JDBC כזה. כמו כן, משאב JMS עשוי להיות בעל מודל דומה אך לא תואם.

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

כ- API, JTA מגדיר ממשקים וסמנטיקה המיושמים על ידי מנהלי עסקאות. יישומים ניתנים על ידי ספריות כגון Narayana ו- Bitronix.

3. דוגמה להגדרת פרויקט

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

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

 org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE org.springframework.boot spring-boot-starter-jta-bitronix 

לבסוף, לפני כל שיטת בדיקה אנו מאותחלים יומן ביקורת עם נתונים ריקים ומסד נתונים חֶשְׁבּוֹן עם 2 שורות:

+ ----------- + ---------------- + | תעודת זהות | איזון | + ----------- + ---------------- + | a0000001 | 1000 | | a0000002 | 2000 | + ----------- + ---------------- +

4. תיחום עסקה הצהרתית

הדרך הראשונה לעבוד עם עסקאות ב- JTA היא השימוש ב- @ Transactional ביאור. לקבלת הסבר ותצורה משוכללים יותר, עיין במאמר זה.

בואו להעריר את שיטת שירות החזיתות executeTranser () עם @ Transactional. זה מורה ל מנהל עסקאות להתחיל עסקה:

@Transactional חלל ציבורי executeTransfer (מחרוזת מ AccountId, מחרוזת toAccountId, סכום BigDecimal) {bankAccountService.transfer (מ AccountId, toAccountId, סכום); auditService.log (fromAccontId, toAccountId, סכום); ...}

הנה השיטה executeTranser () מתקשר לשני שירותים שונים, שירות חשבון ו AuditService. שירותים אלה משתמשים בשני מאגרי מידע שונים.

מתי executeTransfer () החזרות, ה מנהל עסקאות מכיר בכך שזה סוף העסקה ויתחייב לשני מסדי הנתונים:

tellerService.executeTransfer ("a0000001", "a0000002", BigDecimal.valueOf (500)); assertThat (accountService.balanceOf ("a0000001")) .isEqualByComparingTo (BigDecimal.valueOf (500)); assertThat (accountService.balanceOf ("a0000002")) .isEqualByComparingTo (BigDecimal.valueOf (2500)); TransferLog lastTransferLog = auditService .lastTransferLog (); assertThat (lastTransferLog) .isNotNull (); assertThat (lastTransferLog.getFromAccountId ()) .isEqualTo ("a0000001"); assertThat (lastTransferLog.getToAccountId ()) .isEqualTo ("a0000002"); assertThat (lastTransferLog.getAmount ()) .isEqualByComparingTo (BigDecimal.valueOf (500));

4.1. התהפכות בחזרה בגזרה הצהרתית

בסוף השיטה, executeTransfer () בודק את יתרת החשבון וזורק חריגת זמן ריצה אם קרן המקור אינה מספקת:

@Transactional חלל ציבורי executeTransfer (מחרוזת מ AccountId, מחרוזת toAccountId, סכום BigDecimal) {bankAccountService.transfer (מ AccountId, toAccountId, סכום); auditService.log (fromAccontId, toAccountId, סכום); יתרת BigDecimal = bankAccountService.balanceOf (fromAccontId); אם (balance.compareTo (BigDecimal.ZERO) <0) {לזרוק RuntimeException חדש ("קרן לא מספקת."); }}

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

assertThatThrownBy (() -> {tellerService.executeTransfer ("a0000002", "a0000001", BigDecimal.valueOf (10000));}). hasMessage ("קרן לא מספקת."); assertThat (accountService.balanceOf ("a0000001")). isEqualByComparingTo (BigDecimal.valueOf (1000)); assertThat (accountService.balanceOf ("a0000002")). isEqualByComparingTo (BigDecimal.valueOf (2000)); assertThat (auditServie.lastTransferLog ()). isNull ();

5. תיחום עסקאות פרוגרמטי

דרך נוספת לשלוט בעסקת JTA היא באמצעות תוכנה UserTransaction.

עכשיו בואו נשתנה executeTransfer () לטפל בעסקה באופן ידני:

userTransaction.begin (); bankAccountService.transfer (fromAccontId, toAccountId, סכום); auditService.log (fromAccontId, toAccountId, סכום); יתרת BigDecimal = bankAccountService.balanceOf (fromAccontId); אם (balance.compareTo (BigDecimal.ZERO) <0) {userTransaction.rollback (); לזרוק RuntimeException חדש ("לא מספיק קרן."); } אחר {userTransaction.commit (); }

בדוגמה שלנו, ה התחל() השיטה מתחילה עסקה חדשה. אם אימות היתרה נכשל, אנו מתקשרים גלגל לאחור() שיחזור על שני מסדי הנתונים. אחרת, השיחה ל לְבַצֵעַ() מבצע את השינויים בשני מסדי הנתונים.

חשוב לציין כי שניהם לְבַצֵעַ() ו גלגל לאחור() לסיים את העסקה הנוכחית.

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

6. מסקנה

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

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