Hari baik untuk semua!
Yah, akhir bulan selalu intens, dan di sini kita hanya punya satu hari lagi sampai dimulainya aliran kedua kursus
"Pengembang Kerangka Kerja Musim Semi" - kursus yang indah dan menarik yang diajarkan oleh
Yuri yang sama cantik dan marahnya (karena beberapa siswa memanggilnya untuk tingkat persyaratan) di DZ), jadi mari kita lihat materi lain yang telah kami siapkan untuk Anda.
Ayo pergi.
PendahuluanSebagian besar waktu, pengembang tidak mementingkan manajemen transaksi. Akibatnya, sebagian besar kode harus ditulis ulang nanti, atau pengembang mengimplementasikan manajemen transaksi tanpa mengetahui bagaimana seharusnya itu bekerja atau aspek apa yang harus digunakan secara khusus dalam kasus mereka.
Aspek penting dalam manajemen transaksi adalah menentukan batas-batas transaksi yang benar, kapan transaksi harus dimulai dan kapan harus berakhir, kapan data harus ditambahkan ke database dan kapan harus dipompa kembali (dalam kasus pengecualian).

Aspek yang paling penting bagi pengembang adalah memahami cara terbaik menerapkan manajemen transaksi dalam suatu aplikasi. Jadi mari kita lihat berbagai opsi.
Metode Manajemen TransaksiTransaksi dapat dikelola dengan cara berikut:
1. Kontrol program dengan menulis kode khususIni adalah metode manajemen transaksi lama.
EntityManagerFactory factory = Persistence.createEntityManagerFactory("PERSISTENCE_UNIT_NAME"); EntityManager entityManager = entityManagerFactory.createEntityManager(); Transaction transaction = entityManager.getTransaction() try { transaction.begin(); someBusinessCode(); transaction.commit(); } catch(Exception ex) { transaction.rollback(); throw ex; }
Pro :
- Batas-batas transaksi jelas dalam kode.
Cons :
- Itu berulang dan rentan kesalahan.
- Kesalahan apa pun bisa berdampak sangat besar.
- Anda perlu menulis banyak templat, juga, jika Anda ingin memanggil metode lain dari metode ini, Anda perlu mengontrolnya lagi dari kode.
2. Menggunakan Spring untuk Manajemen TransaksionalSpring mendukung dua jenis manajemen transaksi
1. Manajemen Transaksi Perangkat Lunak : Anda harus mengelola transaksi melalui pemrograman. Metode ini cukup fleksibel, tetapi sulit dipertahankan.
2. Manajemen transaksi deklaratif : Anda memisahkan manajemen transaksi dari logika bisnis. Anda hanya menggunakan anotasi dalam konfigurasi berbasis XML untuk manajemen transaksi.
Kami sangat merekomendasikan menggunakan transaksi deklaratif. Jika Anda ingin mengetahui alasannya, maka bacalah, jika tidak, langsung ke bagian Manajemen Transaksi Deklaratif jika Anda ingin menerapkan opsi ini.Sekarang mari kita lihat setiap pendekatan secara rinci.
2.1. Manajemen Transaksi terprogram:Kerangka kerja Spring menyediakan dua alat untuk manajemen transaksi terprogram.
a. Menggunakan
TransactionTemplate
(direkomendasikan oleh tim Spring):
Mari kita lihat bagaimana menerapkan tipe ini menggunakan kode contoh di bawah ini (diambil dari dokumentasi Spring dengan beberapa perubahan)
Perhatikan bahwa cuplikan kode diambil dari Spring Documents.File Konteks Xml:
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/TEST"/> <property name="username" value="root"/> <property name="password" value="password"/> </bean> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean> <bean id="serviceImpl" class="com.service.ServiceImpl"> <constructor-arg ref="transactionManager"/> </bean>
Kelas
Service
:
public class ServiceImpl implements Service { private final TransactionTemplate transactionTemplate;
Jika tidak ada nilai balik, gunakan kelas
TransactionCallbackWithoutResult
nyaman dengan kelas anonim, seperti yang ditunjukkan di bawah ini:
transactionTemplate.execute(new TransactionCallbackWithoutResult() { protected void doInTransactionWithoutResult(TransactionStatus status) { updateOperation1(); updateOperation2(); } });
- Contoh dari kelas
TransactionTemplate
aman di utas, jadi tidak semua kondisi dialog didukung. - Meskipun demikian
TransactionTemplate
instans mendukung konfigurasi negara, jadi jika suatu kelas perlu menggunakan TransactionTemplate dengan pengaturan yang berbeda (misalnya, tingkat isolasi yang berbeda), maka Anda perlu membuat dua instance TransactionTemplate yang berbeda, walaupun beberapa kelas dapat menggunakan instance TransactionTemplate yang sama.
b. Menggunakan implementasi
PlatformTransactionManager
secara langsung:
Mari kita lihat opsi ini dalam kode lagi.
<!-- Initialization for data source --> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/TEST"/> <property name="username" value="root"/> <property name="password" value="password"/> </bean> <!-- Initialization for TransactionManager --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean> public class ServiceImpl implements Service { private PlatformTransactionManager transactionManager; public void setTransactionManager( PlatformTransactionManager transactionManager) { this.transactionManager = transactionManager; } DefaultTransactionDefinition def = new DefaultTransactionDefinition();
Sekarang, sebelum beralih ke metode manajemen transaksi selanjutnya, mari kita lihat bagaimana menentukan jenis manajemen transaksi yang akan dipilih.
Memilih antara
Manajemen Transaksi Programmatis dan
Deklaratif :
- Manajemen transaksi terprogram adalah pilihan yang baik hanya jika Anda memiliki sejumlah kecil operasi transaksional. (Dalam kebanyakan kasus, ini bukan transaksi.)
- Nama transaksi hanya dapat secara eksplisit ditetapkan dalam Program Manajemen Transaksi.
- Manajemen transaksi terprogram harus digunakan ketika Anda ingin secara eksplisit mengontrol manajemen transaksi.
- Di sisi lain, jika aplikasi Anda mengandung banyak operasi transaksional, ada baiknya menggunakan manajemen deklaratif.
- Manajemen deklaratif tidak memungkinkan Anda untuk mengelola transaksi dalam logika bisnis dan tidak sulit untuk dikonfigurasikan.
2.2. Transaksi deklaratif (Biasanya digunakan di hampir semua skenario aplikasi web apa pun)Langkah 1 : Tentukan manajer transaksi dalam file konteks xml aplikasi musim semi Anda.
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"/> <tx:annotation-driven transaction-manager="txManager"/>
Langkah 2 : Aktifkan dukungan anotasi dengan menambahkan entri dalam file xml konteks aplikasi musim semi Anda.
ATAU tambahkan
@EnableTransactionManagement
ke file konfigurasi Anda, seperti yang ditunjukkan di bawah ini:
@Configuration @EnableTransactionManagement public class AppConfig { ... }
Spring merekomendasikan hanya anotasi kelas tertentu (dan metode kelas tertentu) dengan @Transactional
anotasi dibandingkan dengan anotasi antarmuka.Alasan untuk ini adalah karena Anda meletakkan anotasi pada level antarmuka, dan jika Anda menggunakan kelas proxy (
proxy-target-class = «true»
) atau aspek jalinan (
mode = «aspectj»
), maka parameter transaksi tidak dikenali oleh infrastruktur proxy dan pleksus, misalnya Perilaku transaksional tidak akan berlaku.
Langkah 3 : Tambahkan penjelasan
@Transactional
ke kelas (metode kelas) atau antarmuka (metode antarmuka).
<tx:annotation-driven proxy-target-class="true">
Konfigurasi default:
proxy-target-class="false"
@Transactional
dapat ditempatkan sebelum definisi antarmuka, metode antarmuka, definisi kelas, atau metode kelas publik.- Jika Anda ingin beberapa metode kelas (ditandai dengan penjelasan
@Transactional
) memiliki pengaturan atribut yang berbeda, seperti tingkat isolasi atau tingkat propagasi, tempatkan anotasi pada tingkat metode untuk mengganti pengaturan atribut tingkat kelas. - Dalam mode proxy (yang diatur secara default), hanya panggilan metode "eksternal" yang melewati proxy yang dapat disadap. Ini berarti bahwa "panggilan independen", misalnya, metode dalam target yang memanggil beberapa metode lain dari target, tidak akan menghasilkan transaksi aktual pada saat run time bahkan jika metode yang dipanggil ditandai dengan
@Transactional
.
Sekarang mari kita
@Transactional
perbedaan antara
@Transactional
anotasi
@Transactional
@Transactional (isolation=Isolation.READ_COMMITTED)
- Standarnya adalah
Isolation.DEFAULT
- Dalam kebanyakan kasus, Anda akan menggunakan pengaturan default sampai Anda memiliki persyaratan khusus.
- Memberitahu manajer transaksi (
tx
) bahwa level isolasi selanjutnya harus digunakan untuk tx
saat ini. Itu harus diinstal pada titik di mana tx
mulai dari, karena kita tidak dapat mengubah tingkat isolasi setelah tx mulai.
DEFAULT : Gunakan level isolasi default di basis data basis.
READ_COMMITTED (membaca data tetap): Konstanta yang menunjukkan bahwa pembacaan kotor telah dicegah; Dapat terjadi pembacaan yang tidak berulang dan pembacaan hantu.
READ_UNCOMMITTED (baca data yang tidak dikomit): Level isolasi ini menunjukkan bahwa suatu transaksi dapat membaca data yang belum dihapus oleh transaksi lain.
REPEATABLE_READ (baca keterulangan): Konstanta yang menunjukkan bahwa pembacaan yang kotor dan bacaan yang tidak dapat diulang dicegah; pembacaan hantu mungkin muncul.
SERIALISASI : Permanen, menunjukkan bahwa pembacaan yang kotor, pembacaan yang tidak dapat diulang, dan pembacaan hantu dicegah.
Apa arti jargon ini: bacaan “kotor”, bacaan hantu atau bacaan berulang?
- Baca Kotor : Transaksi A menulis. Sementara itu, transaksi "B" membaca catatan yang sama sampai transaksi A. Selesai, transaksi A memutuskan untuk mundur, dan sekarang kami memiliki perubahan pada transaksi B yang tidak kompatibel. Ini bacaan kotor. Transaksi B bekerja pada tingkat isolasi READ_UNCOMMITTED, sehingga dapat membaca perubahan yang dilakukan oleh transaksi A sebelum transaksi selesai.
- Non-Repeatable Read : Transaksi "A" membaca beberapa catatan. Kemudian transaksi "B" menulis catatan ini dan melakukannya. Kemudian, transaksi A membaca catatan yang sama lagi dan dapat menerima nilai yang berbeda, karena transaksi B membuat perubahan pada catatan ini dan memberikannya. Ini adalah bacaan yang tidak berulang.
- Phantom Read : Transaksi "A" membaca serangkaian catatan. Sementara itu, transaksi "B" menyisipkan catatan baru di baris yang sama dengan transaksi A. Kemudian, transaksi A membaca kisaran yang sama lagi dan juga menerima catatan bahwa transaksi B. baru saja dimasukkan. Ini adalah pembacaan hantu: transaksi mengambil serangkaian catatan beberapa kali dari database dan menerima set hasil yang berbeda (berisi catatan hantu).
@Transactional(timeout=60)
Default adalah batas waktu default untuk sistem transaksi yang mendasarinya.
Memberitahu manajer tx tentang lamanya waktu untuk menunggu sampai tx menganggur sebelum memutuskan apakah akan mengembalikan transaksi yang tidak merespons.
@Transactional(propagation=Propagation.REQUIRED)
Jika tidak ditentukan, perilaku propagasi default
REQUIRED
.
Opsi lainnya adalah
REQUIRES_NEW, MANDATORY, SUPPORTS, NOT_SUPPORTED, NEVER
dan
NESTED
.
DIBUTUHKANMenunjukkan bahwa metode target tidak dapat bekerja tanpa tx aktif. Jika tx sudah berjalan sebelum memanggil metode ini, maka tx akan melanjutkan di tx yang sama, atau tx baru akan mulai segera setelah memanggil metode ini.
REQUIRES_NEW- Menunjukkan bahwa tx baru harus dijalankan setiap kali metode target dipanggil. Jika tx sudah berjalan, itu akan dijeda sebelum memulai yang baru.
Mandatory- Menunjukkan bahwa metode target memerlukan tx aktif. Jika tx tidak melanjutkan, itu tidak akan gagal, melempar pengecualian.
DUKUNGAN- Menunjukkan bahwa metode target dapat dieksekusi secara independen dari tx. Jika tx berfungsi, ia akan berpartisipasi dalam tx yang sama. Jika dieksekusi tanpa tx, itu masih akan dieksekusi jika tidak ada kesalahan.
- Metode yang mengambil data adalah kandidat terbaik untuk opsi ini.
NOT_SUPPORTED- Menunjukkan bahwa metode target tidak memerlukan propagasi konteks transaksi.
- Pada dasarnya, metode-metode yang dieksekusi dalam transaksi, tetapi yang melakukan operasi dengan RAM, adalah kandidat terbaik untuk opsi ini.
Tidak pernah- Menunjukkan bahwa metode target akan mengeluarkan pengecualian jika dieksekusi dalam proses transaksional.
- Opsi ini dalam banyak kasus tidak digunakan dalam proyek.
@Transactional (rollbackFor=Exception.class)
Nilai default:
rollbackFor=RunTimeException.class
Di Musim Semi, semua kelas API melempar RuntimeException, yang berarti bahwa jika ada metode gagal, kontainer selalu memutar kembali transaksi saat ini.
Masalahnya hanya dengan pengecualian yang diperiksa. Dengan demikian, parameter ini dapat digunakan untuk memutar kembali transaksi jika
Checked Exception
terjadi.
@Transactional (noRollbackFor=IllegalStateException.class)
Menunjukkan bahwa rollback tidak boleh terjadi jika metode target meningkatkan pengecualian ini.
Sekarang, langkah terakhir tetapi yang paling penting dalam manajemen transaksi adalah memposting anotasi
@Transactiona
. Dalam kebanyakan kasus, kebingungan muncul di mana anotasi harus ditempatkan: di tingkat layanan atau di tingkat DAO?
@Transactional
: Tingkat layanan atau DAO?Layanan adalah tempat terbaik untuk menempatkan
@Transactional
, tingkat layanan harus berisi perilaku use case di tingkat detail untuk interaksi pengguna, yang secara logis masuk ke dalam transaksi.
Ada banyak aplikasi CRUD yang tidak memiliki logika bisnis yang signifikan yang memiliki tingkat layanan yang hanya mentransfer data antara pengontrol dan objek akses data, yang tidak berguna. Dalam kasus ini, kita dapat menempatkan anotasi transaksi di level DAO.
Karena itu, dalam praktiknya, Anda bisa menempatkannya di mana saja, terserah Anda.
Selain itu, jika Anda meletakkan
@Transactional
di lapisan DAO dan jika lapisan DAO Anda akan digunakan kembali oleh layanan yang berbeda, maka akan sulit untuk menempatkannya di lapisan DAO, karena layanan yang berbeda mungkin memiliki persyaratan yang berbeda.
Jika tingkat layanan Anda mengambil objek menggunakan Hibernate, dan katakanlah Anda memiliki inisialisasi malas dalam definisi objek domain, maka Anda perlu membuka transaksi di tingkat layanan, jika tidak, Anda akan menemukan LazyInitializationException yang dilemparkan oleh ORM.
Pertimbangkan contoh lain di mana tingkat layanan Anda dapat memanggil dua metode DAO yang berbeda untuk melakukan operasi basis data. Jika operasi DAO pertama Anda gagal, dua lainnya dapat ditransfer, dan Anda akan mengakhiri keadaan database yang tidak konsisten. Anotasi tingkat layanan dapat menyelamatkan Anda dari situasi seperti itu.
Saya harap artikel ini membantu Anda.
AKHIR
Selalu menarik untuk melihat komentar atau pertanyaan Anda.