Panduan ke Jakarta EE JTA

1. Gambaran keseluruhan

Java Transaction API, lebih dikenal sebagai JTA, adalah API untuk menguruskan transaksi di Java. Ini membolehkan kita memulakan, melakukan dan memutar balik transaksi dengan cara agnostik sumber.

Kekuatan sebenar JTA terletak pada kemampuannya mengurus pelbagai sumber (iaitu pangkalan data, perkhidmatan pesanan) dalam satu transaksi.

Dalam tutorial ini, kita akan mengenal JTA pada tahap konseptual dan melihat bagaimana kod perniagaan biasanya berinteraksi dengan JTA.

2. Universal API dan Transaction Teragih

JTA memberikan penekanan terhadap kawalan transaksi (bermula, komit dan rollback) ke kod perniagaan.

Sekiranya tidak ada abstraksi ini, kita harus berurusan dengan API individu dari setiap jenis sumber.

Sebagai contoh, kita perlu menangani sumber JDBC seperti ini. Begitu juga, sumber JMS mungkin mempunyai model yang serupa tetapi tidak serasi.

Dengan JTA, kita dapat menguruskan pelbagai sumber dari pelbagai jenis secara konsisten dan terselaras .

Sebagai API, JTA mendefinisikan antara muka dan semantik yang akan dilaksanakan oleh pengurus transaksi . Pelaksanaan disediakan oleh perpustakaan seperti Narayana dan Bitronix.

3. Contoh Persediaan Projek

Contoh aplikasi adalah perkhidmatan back-end aplikasi perbankan yang sangat sederhana. Kami mempunyai dua perkhidmatan, BankAccountService dan AuditService menggunakan dua pangkalan data yang berbeza . Pangkalan data bebas ini perlu diselaraskan semasa transaksi bermula, dilakukan atau dilancarkan semula .

Sebagai permulaan, projek sampel kami menggunakan Spring Boot untuk mempermudah konfigurasi:

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

Akhirnya, sebelum setiap kaedah ujian kami menginisialisasi AUDIT_LOG dengan data kosong dan AKAUN pangkalan data dengan 2 baris:

+-----------+----------------+ | ID | BALANCE | +-----------+----------------+ | a0000001 | 1000 | | a0000002 | 2000 | +-----------+----------------+

4. Demarkasi Transaksi Deklaratif

Kaedah pertama untuk melakukan transaksi di JTA adalah dengan menggunakan anotasi @Transactional . Untuk penjelasan dan konfigurasi yang lebih terperinci, lihat artikel ini.

Mari beri penjelasan kaedah perkhidmatan fasad executeTranser () dengan @Transactional. Ini memerintahkan pengurus transaksi untuk memulakan transaksi :

@Transactional public void executeTransfer(String fromAccontId, String toAccountId, BigDecimal amount) { bankAccountService.transfer(fromAccontId, toAccountId, amount); auditService.log(fromAccontId, toAccountId, amount); ... }

Di sini kaedah executeTranser () memanggil 2 perkhidmatan berbeza, AccountService dan AuditService. Perkhidmatan ini menggunakan 2 pangkalan data yang berbeza.

Apabila executeTransfer () pemulangan, yang pengurus transaksi mengakui bahawa ia adalah akhir transaksi dan akan komited untuk kedua-dua pangkalan data :

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. Mengembalikan Demarkasi Deklaratif

Pada akhir kaedah, executeTransfer () memeriksa baki akaun dan melemparkan RuntimeException jika dana sumber tidak mencukupi:

@Transactional public void executeTransfer(String fromAccontId, String toAccountId, BigDecimal amount) { bankAccountService.transfer(fromAccontId, toAccountId, amount); auditService.log(fromAccontId, toAccountId, amount); BigDecimal balance = bankAccountService.balanceOf(fromAccontId); if(balance.compareTo(BigDecimal.ZERO) < 0) { throw new RuntimeException("Insufficient fund."); } }

Yang tak dikendali RuntimeException masa lalu pertama @Transactional akan melakukan pengembalian transaksi untuk kedua-dua pangkalan data . Pada hakikatnya, melaksanakan pemindahan dengan jumlah yang lebih besar daripada kira-kira akan menyebabkan melakukan pengembalian :

assertThatThrownBy(() -> { tellerService.executeTransfer("a0000002", "a0000001", BigDecimal.valueOf(10000)); }).hasMessage("Insufficient fund."); assertThat(accountService.balanceOf("a0000001")).isEqualByComparingTo(BigDecimal.valueOf(1000)); assertThat(accountService.balanceOf("a0000002")).isEqualByComparingTo(BigDecimal.valueOf(2000)); assertThat(auditServie.lastTransferLog()).isNull();

5. Demarkasi Transaksi Terprogram

Kaedah lain untuk mengawal transaksi JTA adalah secara teratur melalui UserTransaction .

Sekarang mari kita ubah executeTransfer () untuk menangani transaksi secara manual:

userTransaction.begin(); bankAccountService.transfer(fromAccontId, toAccountId, amount); auditService.log(fromAccontId, toAccountId, amount); BigDecimal balance = bankAccountService.balanceOf(fromAccontId); if(balance.compareTo(BigDecimal.ZERO) < 0) { userTransaction.rollback(); throw new RuntimeException("Insufficient fund."); } else { userTransaction.commit(); }

Dalam contoh kami, kaedah begin () memulakan transaksi baru. Sekiranya pengesahan keseimbangan gagal, kami memanggil rollback () yang akan rollback ke atas kedua-dua pangkalan data. Jika tidak, panggilan untuk melakukan () melakukan perubahan pada kedua-dua pangkalan data .

Penting untuk diperhatikan bahawa kedua-dua komit () dan rollback () mengakhiri transaksi semasa.

Pada akhirnya, menggunakan pembatasan terprogram memberi kita fleksibiliti kawalan transaksi yang terperinci.

6. Kesimpulannya

Dalam artikel ini, kami membincangkan masalah yang cuba diselesaikan oleh JTA. Contoh kod menggambarkan pengendalian transaksi dengan anotasi dan secara terprogram , melibatkan 2 sumber transaksi yang perlu diselaraskan dalam satu transaksi.

Seperti biasa, contoh kod boleh didapati di GitHub.