Pemahaman mengenai strategi-strategi manajemen transaksi dibutuhkan untuk mempertahankan konsistensi resource yang digunakan. Kesalahan dalam memilih strategi manajemen transaksi tentu saja akan membuat proses bisnis yang diharapkan tidak berjalan dengan semestinya. Mulai dari kegagalan eksekusi hingga integritas database yang rendah.
Beberapa hal yang perlu dimengerti sebelumnya memahami manajemen transaksi ini adalah :
- Konsep ACID (Atomicity, Concistency, Isolation, Durability)
- JTA (Java Transaction API) dan JTS (Java Transaction Service)
Local Transaction Model
Pada Local Transaction Model, manajemen transaksi diatur oleh DBMS. Developer yang mengatur koneksi. Transaksi secara otomatis diatur oleh DBMS karena adanya variabel auto commit yang secara default di-set dengan nilai “true”. Maksudnya adalah setiap kali transaksi terjadi, maka secara otomatis transaksi itu di-commit.
Jika hanya satu eksekusi dalam satu transaksi yang terjadi, maka ini otomatisasi ini mempermudah. Tapi jika dalam satu transaksi terjadi lebih dari satu eksekusi pengubahan resource (Create, Update, Delete) dan antar eksekusi itu memiliki hubungan logik (misalkan penambahan record pada tabel penjualan harus didahului oleh penambahan record pada tabel pemesanan), maka yang terjadi adalah jika eksekusi SQL pertama sukses namun eksekusi SQL kedua gagal, maka yang di-rollback hanyalah eksekusi SQL kedua. Eksekusi SQL yang pertama akan lolos (commited). Ini tidak sesuai dengan konsep ACID. Proses tidak atomic dan resource tidak konsisten.
Untuk menghindari hal itu, maka sebaiknya flag (penanda) auto commit di-set “false” : setAutoCommit(false). Dan eksekusi-eksekusi yang dilakukan terhadap resource dibuat atomic. Sehingga transaksi itu akan bersifat digital : 1/0. Berjalan seluruhnya (commit) atau tidak sama sekali (rollback).
Kemudian pada Local Transaction Model, developer perlu mengatur buka-tutup koneksi. Misalnya membuka koneksi dengan perintah :
DataSource ds = (DataSource) (new InitialContext()).lookup(“jdbc/MasterDataSource”);
Connection conn = ds.getConnection();
Dan menutupnya :
conn.close();
Untuk aplikasi berskala besar, kedua hal ini (membuat auto commit = false dan buka-tutup koneksi) tentu dapat menyulitkan developer. Besar kemungkinan terjadinya kesalahan akibat kelalaian melakukan kedua hal ini. Karena itulah, Local Transaction Model memang disarankan digunakan untuk aplikasi sederhana yang hanya terdiri dari eksekusi-eksekusi resource yang (tentu saja) sederhana. Untuk aplikasi dengan tingkat kerumitan transaksi yang lebih tinggi, sebaiknya menggunakan Programatic ataupun Declarative Transaction (sesuai kebutuhannya) yang akan dibahas sebentar lagi.
Oh ya, satu lagi kekurangan dari Local Transaction Model ini adalah ia tidak mendukung XA Global Transaction.
Programmatic Transaction Model
Pada Programmatic Transaction Model, developer mengatur transaksi, bukan koneksi. Hal ini kebalikan dari Local Transaction Model. Jadi developer bertanggung jawab untuk memulai (begin), mengakhiri (commit), dan rollback suatu transaksi.
Kekurangan Programmatic Transaction Model yang pertama adalah kenyataan bahwa dengan tipe manajemen transaksi ini developer menjadi harus memberikan perhatian ekstra pada exception handling. Mengapa? Karena error yang seringkali terjadi (terutama jika developer lupa membuat “commit“) adalah runtime exception yang tidak terdeteksi ketika melakukan proses pengetesan aplikasi (testing).
Kekurangan kedua dari Programmatic Transaction Model adalah tidak mendukung komunikasi transaction context (transaction context tidak dapat digunakan antar bean). Jadi jika ada dua atau lebih bean yang mempunyai hubungan logik, masing-masing bean akan membuat transaction context sendiri. Data menjadi tidak konsisten ketika bean pertama memanggil bean kedua, dan bean pertama baru akan melakukan eksekusi pada resource setelah eksekusi resource pada bean kedua berhasil dilakukan. Masalahnya, jika saja eksekusi pada bean kedua sukses, namun setelah itu eksekusi pada bean pertama gagal dan harus rollback, perubahan yang terjadi pada bean kedua sudah bersifat permanen. Dus, data menjadi tidak konsisten. Integritas data pun otomatis menurun.
Lalu kapankah Programmatic Transaction Model digunakan?
Programmatic Transaction Model sebaiknya digunakan ketika menghadapi tiga skenario berikut :
- Client-initiated transactions (transaksi yang diinisiasi client) Pada transaksi ini, client membuat banyak (lebih dari satu) remote call untuk sebuah business request. Pada skenario seperti ini, tentu saja dibutuhkan model transaksi yang memungkinkan developer untuk mengatur agar transaksi memungkinkan diinisiasi di sisi client. (menggunakan interface UserTransaction) Karena itulah Programmatic Transaction Model menjadi pilihan. Namun karena pada Programmatic Transaction Model tidak memungkinkan pertukaran transaction context (seperti yang sudah dijelaskan di atas), maka EJB pada client menggunakan Programmatic Transaction Model sedangkan EJB pada remote client ataupun server menggunakan Declarative Transaction Model (yang sebentar lagi akan dijelaskan).
- Localized JTA transactions (lokalisasi transaksi JTA) Ketika alokasi resource yang terpakai amat diperhitungkan, maka diperlukan efisiensi pada penggunaan JTA transaction karena transaksi JTA menghabiskan resource yang cukup banyak dan amat mempengaruhi performansi. Lokalisasi transaksi JTA maksudnya adalah menggunakan transaksi seperlunya saja. Transaksi JTA dimulai hanya sebelum aplikasi akan melakukan perubahan pada resource, dan transaksi JTA tersebut langsung ditutup ketika transaksi sudah selesai. Hal ini tentu saja memerlukan pemodelan transaksi yang memungkinkan fleksibilitas untuk melakukan pemulaian dan pengakhiran transaksi. Dan kebutuhan ini terjawab melalui Programmatic Transaction Model .
- Long-running JTA transactions (transaksi JTA yang berdaur hidup panjang)
Declarative Transaction Model
Tidak seperti Localized Transaction Model yang mengharuskan developer mengatur koneksi ataupun Programmatic Transaction Model yang mewajibkan developer untuk mengatur transaksi, Declaratice Transaction Model membebaskan developer dari kerepotan mengatur koneksi dan transaksi. Pada Programmatic Transaction Model , transaksi “diurus” oleh container, bukan developer. Akan tetapi, si-container ini perlu diberitahu oleh developer mengenai “bagaimana mengatur transaksi tersebut”. Contohnya : kapankah suatu transaksi itu akan dimulai? Method manakah yang membutuhkan transaksi? Apa yang akan container lakukan jika transaksi belum ada?
Bagaimanakah cara memberitahukan hal-hal tersebut? Cara memberitahukannya melalui pengaturan pada XML konfigurasi dari framework yang digunakan. Misalkan pada XML application context di Spring. Pengaturan terletak pada atribut dari transaksi. Atribut transaksi ini digunakan untuk satu buah bean. Namun begitu dalam satu bean dapat saja method-methodnya memiliki sifat atribut yang berbeda. Hal ini dapat dilakukan dengan override atribut transaksi di level method.
Jenis-jenis transaksi yang dapat digunakan pada Declarative Transaction Model adalah :
- Required : Method pada bean membutuhkan transaction context. Jika transaction context sudah diinisiasi container maka transaction context itulah yang akan digunakan. Jika belum maka container akan menginisiasi transaction context baru.
- Mandatory : Method pada bean membutuhkan transaction context yang sudah ada. Jika belum ada transaction context yang diinisiasi oleh container, maka container akan mengeluarkan TransactionRequiredException.
- RequiresNew : Method pada bean membutuhkan transaction context baru. Walaupun sudah ada transaction context yang diinisiasi oleh container, container wajib untuk menginisiasi transaction context baru.
- Supports : Method pada bean membutuhkan tidak membutuhkan transaction context. Tapi jika transaction context sudah ada, maka transaction context tersebut akan digunakan dalam method.
- NotSupported : Method pada bean tidak membutuhkan transaction context. Namun jika transaction context sudah ada, maka transaksi yang sedang berjalan akan ditangguhkan (suspend) sampai method ini selesai dieksekusi. Tipe ini dibutuhkan jika ada suatu proses dalam method yang akan menghasilkan exception jika ada transaction context yang sedang aktif.
- Never : Method pada bean SAMA SEKALI tidak membutuhkan transaction context. Jika transaction context yang aktif dalam container maka akan menghasilkan exception. Tipe ini jarang sekali digunakan.Ada keterbatasan dalam pemilihan tipe atribut transaksi ini. Untuk Entity Bean, tipe atribut transaksi yang dapat dipilih adalah Required, Mandatory, dan RequiresNew. Sedangkan pada Message Driven Bean, tipe atribut yang dapat dipilih hanyalah Required dan NotSupported. Jika EJB mengimplementasikan interface Synchronization maka tipe atribut transaksi yang dapat digunakan adalah Required, Mandatory, dan RequiresNew.
Misalkan dalam suatu method ada dua perintah eksekusi resource. Yang satu adalah eksekusi ke database dan yang satunya lagi eksekusi ke JMS. Eksekusi ke database berjalan dengan baik. Namun kemudian ketika akan mengirimkan email notifikasi ke admin (eksekusi JMS) ternyata server STMP sedang tidak menyala. Sehingga email tidak berhasil dikirim. Kegagalan ini tentu saja akan menghasilkan exception. Nah, jika exception handling tidak diatur dengan baik (apapun exception-nya, pasti akan berujung pada setRollbackOnly() ) maka hanya karena kegagalan mengirim email notifikasi segala eksekusi yang telah dilakukan pun ditolak (rollback). Padahal, bisa saja developer mengakali kondisi ini dengan mengirimkan email notifikasi nanti, setelah server STMP aktif (suspend atau on schedule). Dan eksekusi resource ke database menjadi permanen. Triknya adalah dengan membuat penanganan exception dengan benar. Tidak semua exception langsung mengakibatkan setRollbackOnly().