Memahami transaksi XA
Microsoft JDBC Driver untuk SQL Server menyediakan dukungan untuk transaksi terdistribusi opsional Java Platform, Enterprise Edition/JDBC 2.0. Koneksi JDBC yang diperoleh dari kelas SQLServerXADataSource dapat berpartisipasi dalam lingkungan pemrosesan transaksi terdistribusi standar seperti server aplikasi Java Platform, Enterprise Edition (Java EE).
Dalam artikel ini, XA adalah singkatan dari arsitektur yang diperluas.
Peringatan
Microsoft JDBC Driver 4.2 (dan yang lebih tinggi) untuk SQL menyertakan opsi batas waktu baru untuk fitur yang ada untuk pemutaran kembali otomatis transaksi yang tidak siap. Lihat Mengonfigurasi pengaturan batas waktu sisi server untuk pemutaran otomatis transaksi yang tidak siap nanti dalam topik ini untuk detail selengkapnya.
Keterangan
Kelas untuk implementasi transaksi terdistribusi adalah sebagai berikut:
Kelas | Penerapan | Deskripsi |
---|---|---|
com.microsoft.sqlserver.jdbc.SQLServerXADataSource | javax.sql.XADataSource | Pabrik kelas untuk koneksi terdistribusi. |
com.microsoft.sqlserver.jdbc.SQLServerXAResource | javax.transaction.xa.XAResource | Adaptor sumber daya untuk manajer transaksi. |
Catatan
Koneksi transaksi terdistribusi XA default ke tingkat isolasi Read Committed.
Panduan dan batasan saat menggunakan transaksi XA
Panduan tambahan berikut berlaku untuk transaksi yang digabungkan dengan erat:
Saat Anda menggunakan transaksi XA bersama dengan Koordinator Transaksi Terdistribusi Microsoft (MS DTC), Anda mungkin melihat bahwa versi MS DTC saat ini tidak mendukung perilaku cabang XA yang digabungkan dengan erat. Misalnya, MS DTC memiliki pemetaan satu-ke-satu antara ID transaksi cabang XA (XID) dan ID transaksi MS DTC dan pekerjaan yang secara longgar menggabungkan cabang XA melakukan terisolasi satu sama lain.
MS DTC juga mendukung cabang XA yang digabungkan dengan erat di mana beberapa cabang XA dengan ID transaksi global (GTRID) yang sama dipetakan ke satu ID transaksi MS DTC. Dukungan ini memungkinkan beberapa cabang XA yang digabungkan dengan erat untuk melihat perubahan satu sama lain di manajer sumber daya, seperti SQL Server.
Bendera SSTRANSTIGHTLYCPLD memungkinkan aplikasi untuk menggunakan transaksi XA yang digabungkan erat, yang memiliki ID transaksi cabang XA (BQUAL) yang berbeda tetapi memiliki ID transaksi global (GTRID) dan ID format (FormatID) yang sama. Untuk menggunakan fitur tersebut , Anda harus mengatur SSTRANSTIGHTLYCPLD pada parameter bendera metode XAResource.start:
xaRes.start(xid, SQLServerXAResource.SSTRANSTIGHTLYCPLD);
Instruksi Konfigurasi
Langkah-langkah berikut diperlukan jika Anda ingin menggunakan sumber data XA bersama dengan Koordinator Transaksi Terdistribusi Microsoft (MS DTC) untuk menangani transaksi terdistribusi. Langkah-langkah tingkat tinggi adalah:
- Pastikan layanan MS DTC berjalan dan dimulai secara otomatis.
- Mengonfigurasi komponen sisi server.
- Mengonfigurasi batas waktu sisi server (opsional).
- Memberikan akses kepada pengguna.
Menjalankan layanan MS DTC
Layanan MS DTC harus ditandai Otomatis di Manajer Layanan untuk memastikan bahwa layanan tersebut berjalan saat layanan SQL Server dimulai. Untuk mengaktifkan MS DTC untuk transaksi XA, Anda harus mengikuti langkah-langkah berikut:
Pada Windows Vista dan yang lebih baru:
Pilih tombol Mulai , ketik dcomcnfg di kotak Mulai Pencarian , lalu tekan ENTER untuk membuka Layanan Komponen. Anda juga dapat mengetik
%windir%\system32\comexp.msc
dalam kotak StartSearch untuk membuka Layanan Komponen.Perluas Layanan Komponen, Komputer, Komputer Saya, lalu Koordinator Transaksi Terdistribusi.
Klik kanan DTC Lokal lalu pilih Properti.
Pilih tab Keamanan pada kotak dialog Properti DTC Lokal.
Pilih kotak centang Aktifkan Transaksi XA, lalu pilih OK. Tindakan ini menyebabkan mulai ulang layanan MS DTC.
Pilih OK lagi untuk menutup kotak dialog Properti , lalu tutup Layanan Komponen.
Hentikan lalu mulai ulang SQL Server untuk memastikan bahwa SQL Server disinkronkan dengan perubahan MS DTC. (Langkah ini bersifat opsional untuk SQL Server 2019 dan SQL Server 2017 CU 16 dan yang lebih tinggi.)
Mengonfigurasi komponen transaksi terdistribusi JDBC
Langkah-langkah untuk mengonfigurasi komponen sisi server berbeda tergantung pada versi server target Anda. Untuk memeriksa versi server Anda, jalankan kueri SELECT @@VERSION
terhadap server dan tampilkan output. Untuk SQL Server 2017 Cumulative Update (CU) 16 dan yang lebih tinggi, ikuti SQL Server 2017 CU16 dan instruksi yang lebih tinggi . Untuk versi SQL Server yang lebih lama, ikuti SQL Server 2017 CU15 dan instruksi yang lebih rendah .
SQL Server 2017 CU16 dan yang lebih tinggi
Untuk mengaktifkan komponen yang diperlukan untuk melakukan transaksi terdistribusi XA menggunakan driver JDBC, jalankan prosedur tersimpan berikut.
EXEC sp_sqljdbc_xa_install
Untuk menonaktifkan komponen, jalankan prosedur tersimpan berikut.
EXEC sp_sqljdbc_xa_uninstall
Lewati ke bagian Mengonfigurasi pengaturan batas waktu sisi server untuk pembatalan otomatis transaksi yang tidak siap.
SQL Server 2017 CU15 dan yang lebih rendah
Catatan
Ini hanya berlaku untuk SQL Server 2017 CU15 dan yang lebih rendah. Fungsi yang disediakan oleh sqljdbc_xa.dll sudah disertakan dalam SQL Server 2017 CU16 dan yang lebih tinggi.
Komponen transaksi terdistribusi JDBC disertakan dalam direktori xa penginstalan driver JDBC. Komponen-komponen ini mencakup file xa_install.sql dan sqljdbc_xa.dll. Jika Anda memiliki versi driver JDBC yang berbeda pada klien yang berbeda, disarankan untuk menggunakan sqljdbc_xa.dll terbaru di server.
Anda dapat mengonfigurasi komponen transaksi terdistribusi driver JDBC dengan mengikuti langkah-langkah berikut:
Salin sqljdbc_xa.dll baru dari direktori penginstalan driver JDBC ke direktori Binn dari setiap komputer SQL Server yang berpartisipasi dalam transaksi terdistribusi.
Catatan
Jika Anda menggunakan transaksi XA dengan SQL Server 32-bit (hanya berlaku untuk SQL Server 2014 atau yang lebih lama), gunakan file sqljdbc_xa.dll di folder x86, bahkan jika SQL Server diinstal pada prosesor x64. Jika Anda menggunakan transaksi XA dengan SQL Server 64-bit pada prosesor x64, gunakan file sqljdbc_xa.dll di folder x64.
Jalankan xa_install.sql skrip database pada setiap instans SQL Server yang berpartisipasi dalam transaksi terdistribusi. Skrip ini menginstal prosedur tersimpan yang diperluas yang dipanggil oleh sqljdbc_xa.dll. Prosedur tersimpan yang diperluas ini menerapkan transaksi terdistribusi dan dukungan XA untuk Microsoft JDBC Driver untuk SQL Server. Anda perlu menjalankan skrip ini sebagai administrator instans SQL Server.
Untuk memberikan izin kepada pengguna tertentu untuk berpartisipasi dalam transaksi terdistribusi dengan driver JDBC, tambahkan pengguna ke peran SqlJDBCXAUser.
Anda hanya dapat mengonfigurasi satu versi perakitan sqljdbc_xa.dll pada setiap instans SQL Server sekaligus. Aplikasi mungkin perlu menggunakan versi driver JDBC yang berbeda untuk terhubung ke instans SQL Server yang sama dengan menggunakan koneksi XA. Dalam hal ini, sqljdbc_xa.dll, yang dilengkapi dengan driver JDBC terbaru, harus diinstal pada instans SQL Server.
Ada tiga cara untuk memverifikasi versi sqljdbc_xa.dll yang saat ini diinstal pada instans SQL Server:
Buka direktori LOG komputer SQL Server yang berpartisipasi dalam transaksi terdistribusi. Pilih dan buka file "ERRORLOG" SQL Server. Cari versi "Menggunakan 'SQLJDBC_XA.dll' ..." frasa dalam file "ERRORLOG".
Buka direktori Binn komputer SQL Server yang berpartisipasi dalam transaksi terdistribusi. Pilih rakitan sqljdbc_xa.dll.
- Pada Windows Vista atau yang lebih baru: Klik kanan sqljdbc_xa.dll lalu pilih Properti. Lalu pilih tab Detail . Bidang Versi File memperlihatkan versi sqljdbc_xa.dll yang saat ini diinstal pada instans SQL Server.
Atur fungsionalitas pengelogan seperti yang ditunjukkan dalam contoh kode di bagian berikutnya. Cari "Server XA DLL versi:..." frasa dalam file log output.
Meningkatkan sqljdbc_xa.dll
Catatan
Ini hanya berlaku untuk SQL Server 2017 CU15 dan yang lebih rendah. Fungsi yang disediakan oleh sqljdbc_xa.dll sudah disertakan dalam SQL Server 2017 CU16 dan yang lebih tinggi.
Ketika Anda menginstal versi baru driver JDBC, Anda juga harus menggunakan sqljdbc_xa.dll dari versi baru untuk meningkatkan sqljdbc_xa.dll di server.
Penting
Anda harus meningkatkan sqljdbc_xa.dll selama jendela pemeliharaan atau ketika tidak ada transaksi MS DTC yang sedang berlangsung.
Bongkar sqljdbc_xa.dll menggunakan perintah Transact-SQL:
DBCC sqljdbc_xa (FREE)
Salin sqljdbc_xa.dll baru dari direktori penginstalan driver JDBC ke direktori Binn dari setiap komputer SQL Server yang berpartisipasi dalam transaksi terdistribusi.
DLL baru dimuat ketika prosedur yang diperluas di sqljdbc_xa.dll dipanggil. Anda tidak perlu memulai ulang SQL Server untuk memuat definisi baru.
Mengonfigurasi pengaturan batas waktu sisi server untuk pembatalan otomatis transaksi yang tidak siap
Ada dua pengaturan registri (nilai DWORD) untuk mengontrol perilaku batas waktu transaksi terdistribusi:
XADefaultTimeout
(dalam detik): Nilai batas waktu default yang akan digunakan saat pengguna tidak menentukan batas waktu apa pun. Defaultnya adalah 0.XAMaxTimeout
(dalam detik): Nilai maksimum batas waktu yang dapat diatur pengguna. Defaultnya adalah 0.
Pengaturan ini khusus untuk instans SQL Server dan harus dibuat di bawah kunci registri berikut:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\MSSQL<version>.<instance_name>\XATimeout
Catatan
Untuk SQL Server 32-bit yang berjalan di komputer 64-bit (hanya berlaku untuk SQL Server 2014 dan yang lebih lama), pengaturan registri harus dibuat di bawah kunci berikut: HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Microsoft SQL Server\MSSQL<version>.<instance_name>\XATimeout
Nilai batas waktu diatur untuk setiap transaksi saat dimulai dan SQL Server mengembalikan transaksi jika batas waktu berakhir. Batas waktu ditentukan tergantung pada pengaturan registri ini dan tergantung pada apa yang telah ditentukan pengguna melalui XAResource.setTransactionTimeout(). Beberapa contoh tentang bagaimana nilai batas waktu ini ditafsirkan sebagai berikut:
XADefaultTimeout = 0
,XAMaxTimeout = 0
Berarti tidak ada batas waktu default yang digunakan, dan tidak ada batas waktu maksimum yang diberlakukan pada klien. Dalam hal ini, transaksi memiliki batas waktu hanya jika klien menetapkan batas waktu menggunakan XAResource.setTransactionTimeout.
XADefaultTimeout = 60
,XAMaxTimeout = 0
Berarti semua transaksi memiliki batas waktu 60 detik jika klien tidak menentukan batas waktu. Jika klien menentukan batas waktu, maka nilai batas waktu tersebut digunakan. Tidak ada nilai maksimum untuk batas waktu yang diberlakukan.
XADefaultTimeout = 30
,XAMaxTimeout = 60
Berarti semua transaksi memiliki batas waktu 30 detik jika klien tidak menentukan batas waktu. Jika klien menentukan batas waktu, maka batas waktu klien digunakan selama kurang dari 60 detik (nilai maksimum).
XADefaultTimeout = 0
,XAMaxTimeout = 30
Berarti semua transaksi memiliki batas waktu 30 detik (nilai maksimum) jika klien tidak menentukan batas waktu. Jika klien menentukan batas waktu, maka batas waktu klien digunakan selama kurang dari 30 detik (nilai maksimum).
Mengonfigurasi peran yang ditentukan pengguna
Untuk memberikan izin kepada pengguna tertentu untuk berpartisipasi dalam transaksi terdistribusi dengan driver JDBC, tambahkan pengguna ke peran SqlJDBCXAUser. Misalnya, gunakan kode Transact-SQL berikut untuk menambahkan pengguna bernama 'shelly' (pengguna login standar SQL bernama 'shelly') ke peran SqlJDBCXAUser:
USE master
GO
EXEC sp_grantdbaccess 'shelly', 'shelly'
GO
EXEC sp_addrolemember [SqlJDBCXAUser], 'shelly'
Peran yang ditentukan pengguna SQL ditentukan per database. Untuk membuat peran Anda sendiri untuk tujuan keamanan, Anda harus menentukan peran di setiap database, dan menambahkan pengguna dengan cara per database. Peran SqlJDBCXAUser didefinisikan secara ketat dalam database master karena digunakan untuk memberikan akses ke prosedur tersimpan yang diperluas SQL JDBC yang berada di master. Anda harus terlebih dahulu memberikan akses pengguna individual ke master, lalu memberi mereka akses ke peran SqlJDBCXAUser saat Anda masuk ke database master.
Contoh
import java.net.Inet4Address;
import java.sql.*;
import java.util.Random;
import javax.sql.XAConnection;
import javax.transaction.xa.*;
import com.microsoft.sqlserver.jdbc.*;
public class testXA {
public static void main(String[] args) throws Exception {
// Create variables for the connection string.
String prefix = "jdbc:sqlserver://";
String serverName = "localhost";
int portNumber = 1433;
String databaseName = "AdventureWorks";
String user = "UserName";
String password = "*****";
String connectionUrl = prefix + serverName + ":" + portNumber + ";encrypt=true;databaseName=" + databaseName + ";user="
+ user + ";password=" + password;
Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
try (Connection con = DriverManager.getConnection(connectionUrl); Statement stmt = con.createStatement()) {
stmt.executeUpdate("CREATE TABLE XAMin (f1 int, f2 varchar(max))");
}
// Create the XA data source and XA ready connection.
SQLServerXADataSource ds = new SQLServerXADataSource();
ds.setUser(user);
ds.setPassword(password);
ds.setServerName(serverName);
ds.setPortNumber(portNumber);
ds.setDatabaseName(databaseName);
XAConnection xaCon = ds.getXAConnection();
try (Connection con = xaCon.getConnection()) {
// Get a unique Xid object for testing.
XAResource xaRes = null;
Xid xid = null;
xid = XidImpl.getUniqueXid(1);
// Get the XAResource object and set the timeout value.
xaRes = xaCon.getXAResource();
xaRes.setTransactionTimeout(0);
// Perform the XA transaction.
System.out.println("Write -> xid = " + xid.toString());
xaRes.start(xid, XAResource.TMNOFLAGS);
PreparedStatement pstmt = con.prepareStatement("INSERT INTO XAMin (f1,f2) VALUES (?, ?)");
pstmt.setInt(1, 1);
pstmt.setString(2, xid.toString());
pstmt.executeUpdate();
// Commit the transaction.
xaRes.end(xid, XAResource.TMSUCCESS);
xaRes.commit(xid, true);
}
xaCon.close();
// Open a new connection and read back the record to verify that it worked.
try (Connection con = DriverManager.getConnection(connectionUrl); Statement stmt = con.createStatement();
ResultSet rs = stmt.executeQuery("SELECT * FROM XAMin")) {
rs.next();
System.out.println("Read -> xid = " + rs.getString(2));
stmt.executeUpdate("DROP TABLE XAMin");
}
}
}
class XidImpl implements Xid {
public int formatId;
public byte[] gtrid;
public byte[] bqual;
public byte[] getGlobalTransactionId() {
return gtrid;
}
public byte[] getBranchQualifier() {
return bqual;
}
public int getFormatId() {
return formatId;
}
XidImpl(int formatId, byte[] gtrid, byte[] bqual) {
this.formatId = formatId;
this.gtrid = gtrid;
this.bqual = bqual;
}
public String toString() {
int hexVal;
StringBuffer sb = new StringBuffer(512);
sb.append("formatId=" + formatId);
sb.append(" gtrid(" + gtrid.length + ")={0x");
for (int i = 0; i < gtrid.length; i++) {
hexVal = gtrid[i] & 0xFF;
if (hexVal < 0x10)
sb.append("0" + Integer.toHexString(gtrid[i] & 0xFF));
else
sb.append(Integer.toHexString(gtrid[i] & 0xFF));
}
sb.append("} bqual(" + bqual.length + ")={0x");
for (int i = 0; i < bqual.length; i++) {
hexVal = bqual[i] & 0xFF;
if (hexVal < 0x10)
sb.append("0" + Integer.toHexString(bqual[i] & 0xFF));
else
sb.append(Integer.toHexString(bqual[i] & 0xFF));
}
sb.append("}");
return sb.toString();
}
// Returns a globally unique transaction id.
static byte[] localIP = null;
static int txnUniqueID = 0;
static Xid getUniqueXid(int tid) {
Random rnd = new Random(System.currentTimeMillis());
txnUniqueID++;
int txnUID = txnUniqueID;
int tidID = tid;
int randID = rnd.nextInt();
byte[] gtrid = new byte[64];
byte[] bqual = new byte[64];
if (null == localIP) {
try {
localIP = Inet4Address.getLocalHost().getAddress();
} catch (Exception ex) {
localIP = new byte[] {0x01, 0x02, 0x03, 0x04};
}
}
System.arraycopy(localIP, 0, gtrid, 0, 4);
System.arraycopy(localIP, 0, bqual, 0, 4);
// Bytes 4 -> 7 - unique transaction id.
// Bytes 8 ->11 - thread id.
// Bytes 12->15 - random number generated by using seed from current time in milliseconds.
for (int i = 0; i <= 3; i++) {
gtrid[i + 4] = (byte) (txnUID % 0x100);
bqual[i + 4] = (byte) (txnUID % 0x100);
txnUID >>= 8;
gtrid[i + 8] = (byte) (tidID % 0x100);
bqual[i + 8] = (byte) (tidID % 0x100);
tidID >>= 8;
gtrid[i + 12] = (byte) (randID % 0x100);
bqual[i + 12] = (byte) (randID % 0x100);
randID >>= 8;
}
return new XidImpl(0x1234, gtrid, bqual);
}
}