Útmutató Jakarta EE JTA-hoz
1. Áttekintés
Java Transaction API, közismertebb nevén JTA, egy API a Java tranzakciók kezelésére. Lehetővé teszi számunkra, hogy erőforrás-agnosztikus módon indítsunk, hajtsunk végre és visszavonjunk tranzakciókat.
A JTA valódi ereje abban rejlik, hogy több erőforrást (azaz adatbázisokat, üzenetküldő szolgáltatásokat) egyetlen tranzakcióban kezelhet.
Ebben az oktatóanyagban fogalmi szinten megismerjük a JTA-t, és meglátjuk, hogy az üzleti kód hogyan lép kölcsönhatásba a JTA-val.
2. Univerzális API és elosztott tranzakció
A JTA absztrakciót biztosít az üzleti kód tranzakcióvezérléséhez (kezdet, véglegesítés és visszagörgetés).
Ennek az absztrakciónak a hiányában az egyes erőforrástípusok egyedi API -ival kell foglalkoznunk.
Például az ilyen JDBC erőforrásokkal kell foglalkoznunk. Hasonlóképpen, egy JMS erőforrásnak is lehet hasonló, de nem kompatibilis modellje.
A JTA-val megtehetjük több, különböző típusú erőforrás kezelése következetes és összehangolt módon.
API-ként a JTA meghatározza az általuk megvalósítandó interfészeket és szemantikákat tranzakciókezelők. A megvalósításokat olyan könyvtárak biztosítják, mint a Narayana és a Bitronix.
3. Minta projektbeállítás
A mintaalkalmazás egy banki alkalmazás nagyon egyszerű háttérszolgáltatása. Két szolgáltatásunk van, a BankAccountService és AuditService két különböző adatbázis felhasználásával. Ezeket a független adatbázisokat össze kell hangolni a tranzakció megkezdésekor, végrehajtásakor vagy visszagörgetésekor.
Először is, mintaprojektünk a Spring Boot programot használja a konfiguráció egyszerűsítésére:
org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE org.springframework.boot spring-boot-starter-jta-bitronix
Végül az egyes vizsgálati módszerek előtt inicializáljuk AUDIT_LOG üres adatokkal és adatbázissal SZÁMLA 2 sorral:
+ ----------- + ---------------- + | ID | MÉRLEG | + ----------- + ---------------- + | a0000001 | 1000 | | a0000002 | 2000 | + ----------- + ---------------- +
4. Nyilatkozati ügylethatárolás
A JTA-ban végzett tranzakciók kezelésének első módja a @ Tranzakció annotáció. Részletesebb magyarázat és konfiguráció a cikkben található.
Jegyezzük fel a homlokzati szolgáltatási módszert executeTranser () val vel @ Tranzakció. Ez utasítja a tranzakciókezelő hogy tranzakcióba kezdjen:
@Transactional public void executeTransfer (String fromAccontId, String toAccountId, BigDecimal summa) {bankAccountService.transfer (fromAccontId, toAccountId, összeg); auditService.log (fromAccontId, toAccountId, összeg); ...}
Itt a módszer executeTranser () 2 különböző szolgáltatást hív, AccountService és AuditService. Ezek a szolgáltatások 2 különböző adatbázist használnak.
Mikor executeTransfer () visszatér, a tranzakciókezelő elismeri, hogy a tranzakció vége, és elkötelezi magát mindkét adatbázis mellett:
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. Visszagörgetés a deklaratív elhatárolásban
A módszer végén executeTransfer () ellenőrzi a számlaegyenleget és dob RuntimeException ha a forrásalap nem elegendő:
@Transactional public void executeTransfer (String fromAccontId, String toAccountId, BigDecimal summa) {bankAccountService.transfer (fromAccontId, toAccountId, összeg); auditService.log (fromAccontId, toAccountId, összeg); BigDecimal egyenleg = bankAccountService.balanceOf (fromAccontId); if (balance.compareTo (BigDecimal.ZERO) <0) {dobjon új RuntimeException-t ("Elégtelen forrás"); }}
An kezeletlenül RuntimeException az elsőn túl @ Tranzakció visszavonja a tranzakciótmindkét adatbázisba. Valójában az egyenlegnél nagyobb összegű átutalás visszagörgetést okoz:
assertThatThrownBy (() -> {tellerService.executeTransfer ("a0000002", "a0000001", BigDecimal.valueOf (10000));}). hasMessage ("Elégtelen az alap."); assertThat (accountService.balanceOf ("a0000001")). isEqualByComparingTo (BigDecimal.valueOf (1000)); assertThat (accountService.balanceOf ("a0000002")). isEqualByComparingTo (BigDecimal.valueOf (2000)); assertThat (auditServie.lastTransferLog ()). isNull ();
5. Programozott tranzakciók elhatárolása
A JTA tranzakciók ellenőrzésének másik módja a programon keresztül UserTransaction.
Most módosítsunk executeTransfer () a tranzakció kézi kezeléséhez:
userTransaction.begin (); bankAccountService.transfer (fromAccontId, toAccountId, összeg); auditService.log (fromAccontId, toAccountId, összeg); BigDecimal egyenleg = bankAccountService.balanceOf (fromAccontId); if (balance.compareTo (BigDecimal.ZERO) <0) {userTransaction.rollback (); dobjon új RuntimeException-t ("Elégtelen az alap."); } else {userTransaction.commit (); }
Példánkban a kezdődik() módszer új tranzakciót indít. Ha az egyenleg érvényesítése nem sikerül, hívjuk visszagörgetés () amely mindkét adatbázison átgördül. Másképp, a hívás elkövetni() mindkét adatbázisban végrehajtja a változtatásokat.
Fontos megjegyezni, hogy mindkettő elkövetni() és visszagörgetés () az aktuális tranzakció befejezése.
Végül az automatizált elhatárolás használata rugalmasságot biztosít számunkra a finom tranzakciók ellenőrzésében.
6. Következtetés
Ebben a cikkben megvitattuk azt a problémát, amelyet a JTA megpróbál megoldani. A kód példák illusztrálja a tranzakció vezérlését kommentárokkal és programozottan, 2 tranzakciós erőforrást érintve, amelyeket egyetlen tranzakcióban kell koordinálni.
Szokás szerint a kódpélda megtalálható a GitHub oldalon.