Microsoft SQL Server 2005 JDBC Driver 提供 J2EE/JDBC 2.0 選擇性分散式交易的支援。從 SQLServerXADataSource 類別取得的 JDBC 連接可參與標準分散式交易處理環境,例如 J2EE 應用程式伺服器。
分散式交易實作的類別如下:
| 類別 | 實作 | 描述 |
|---|---|---|
com.microsoft.sqlserver.jdbc.SQLServerXADataSource |
javax.sql.XADataSource |
分散式連接的 Class Factory。 |
com.microsoft.sqlserver.jdbc.SQLServerXAResource |
javax.transaction.xa.XAResource |
交易管理員的資源配接器。 |
注意
XA 分散式交易連接會預設為「讀取認可」隔離等級。
當您透過 Microsoft SQL Server 2005 JDBC 驅動程式,搭配 SQL Server 使用 XA 交易時,您可能會注意到效能降低。只有在參與 XA 交易的 SQL Server 在 Windows XP 上執行時,這個問題才適用。在另一方面,在連接到不在 Windows XP 上執行之遠端 SQL Server 的 Windows XP 上執行的用戶端應用程式可以參與 XA 交易。如需有關如何解決這個問題的詳細資訊,請參閱在<效能和 SQL Server>提供的 Hotfix。
當您將 XA 交易與 Windows Server 2003 上的 Microsoft 分散式交易協調器 (MS DTC) 一起使用時,您可能會注意到 XAResource.setTransactionTimeout 方法沒有作用。若要解決這個問題,您必須將<MSDTC 和 XA 將易>(英文) 提供的 Hotfix 套用到參與 XA 交易的每部 SQL Server 電腦。如果沒有這個修正程式,唯一有效的逾時值為預設值 0,表示無限逾時。
當您將 XA 交易與 Microsoft 分散式交易協調器 (MS DTC) 一起使用時,您可能會注意到目前版本的 MS DTC 不支援緊密繫結的 XA 分支行為。例如,MS DTC 在 XA 分支交易識別碼 (XID) 與 MS DTC 交易識別碼之間擁有一對一的對應,而且由鬆散繫結之 XA 分支所執行的工作會彼此隔離。
在<MSDTC 和緊密繫結的交易>(英文) 所提供的 Hotfix 可以支援緊密繫結的 XA 分支,其中具有相同全域交易識別碼 (GTRID) 的多個 XA 分支會對應到單一 MS DTC 交易識別碼。 這個支援可讓多個緊密繫結的 XA 分支查看彼此在資源管理員 (例如,SQL Server) 中的變更。
Microsoft SQL Server 2005 JDBC 驅動程式 1.2 版現在提供 SSTRANSTIGHTLYCPLD 旗標,讓應用程式使用 XA 分支交易識別碼 (XID) 不同,但全域交易識別碼 (GTRID) 相同之緊密繫結的 XA 交易。若要使用該功能,您必須在 XAResource.start 方法的 flags 參數上,設定 SSTRANSTIGHTLYCPLD。
xaRes.start(xid, SQLServerXAResource.SSTRANSTIGHTLYCPLD);
組態指示
如果要將 XA 資料來源與 Microsoft 分散式交易協調器 (MS DTC) 搭配使用,來處理分散式交易,則需要下列步驟。
注意
JDBC 分散式交易元件會包含在 JDBC 驅動程式安裝的 xa 目錄中。這些元件包括 xa_install.sql 及 sqljdbc_xa.dll 檔案。
執行 MS DTC 服務
應該在服務管理員中將 MS DTC 服務標示為 [自動],以確定啟動 SQL Server 服務時,便執行該服務。若要啟用 MS DTC 以用於 XA 交易,您必須遵循下列步驟:
從 [控制台] 開啟 [系統管理工具]****,然後開啟 [元件服務]。
展開 [元件服務],以滑鼠右鍵按一下 [我的電腦]****,然後選取 [內容]。
按一下 [MSDTC]**** 索引標籤,然後按一下 [安全性組態]。
選取 [啟用 XA 交易]**** 核取方塊,然後按一下 [確定]。這將導致 MS DTC 服務重新啟動。
再按一下 [確定],以關閉 [內容] 對話方塊,然後關閉 [元件服務]。
停止然後重新啟動 SQL Server,以確保其與 MS DTC 變更保持同步。
設定 JDBC 分散式交易元件
您可以遵循下列步驟,來設定 JDBC 驅動程式分散式交易元件:
將 sqljdbc_xa.dll 從 JDBC 安裝目錄複製到將參與分散式交易之每一部 SQL Server 電腦的 Binn 目錄。
注意
如果您搭配 32 位元的 SQL Server 使用 XA 交易,即使 SQL Server 安裝在 x64 處理器上,也請使用 x86 資料夾中的 sqljdbc_xa.dll 檔。如果您在 x64 處理器上,搭配 64 位元的 SQL Server 使用 XA 交易,請使用 x64 資料夾中的 sqljdbc_xa.dll 檔。如果您在 IA-64 處理器上,搭配 64 位元的 SQL Server 使用 XA 交易,請使用 IA64 資料夾中的 sqljdbc_xa.dll 檔。
在每一個將參與分散式交易的 SQL Server 執行個體上執行資料庫指令碼 xa_install.sql。此指令碼會將 sqljdbc_xa.dll 安裝成擴充預存程序。您將需要以 SQL Server 執行個體的系統管理員身分執行此指令碼。
若要授與權限給特定使用者以使用 JDBC 驅動程式參與分散式交易,請將該使用者新增至 SqlJDBCXAUser 角色。
設定使用者自訂角色
若要授與權限給特定使用者以使用 JDBC 驅動程式參與分散式交易,請將該使用者新增至 SqlJDBCXAUser 角色。例如,使用下列 Transact-SQL 程式碼,將名為 'shelby' (SQL 標準的登入使用者名稱為 'shelby') 的使用者新增至 SqlJDBCXAUser 角色:
USE master
GO
EXEC sp_grantdbaccess 'shelby', 'shelby'
GO
EXEC sp_addrolemember [SqlJDBCXAUser], 'shelby'
每一個資料庫都會定義 SQL 使用者自訂角色。若要基於安全目的來建立您自己的角色,您將必須在每一個資料庫中定義角色,然後在每一個資料庫中新增使用者。SqlJDBCXAUser 角色一定是定義在 master 資料庫中,因為它是用來授與存取 SQL JDBC 擴充預存程序 (位於 master 資料庫) 的權限。您必須先授與個別使用者存取 master 的權限,然後在您登入 master 資料庫時,再授與他們存取 SqlJDBCXAUser 角色的權限。
範例
import java.net.Inet4Address;
import java.sql.*;
import java.util.Random;
import javax.transaction.xa.*;
import javax.sql.*;
import com.microsoft.sqlserver.jdbc.*;
public class testXA {
public static void main(String[] args) throws Exception {
// Create a variable for the connection string.
String connectionUrl = "jdbc:sqlserver://localhost:1433;"
+"databaseName=AdventureWorks;user=UserName;password=*****";
try {
// Establish the connection.
Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
Connection con = DriverManager.getConnection(connectionUrl);
// Create a test table.
Statement stmt = con.createStatement();
try {stmt.executeUpdate("DROP TABLE XAMin"); }catch (Exception e) {}
stmt.executeUpdate("CREATE TABLE XAMin (f1 int, f2 varchar(max))");
stmt.close();
con.close();
// Create the XA data source and XA ready connection.
SQLServerXADataSource ds = new SQLServerXADataSource();
ds.setUser("UserName");
ds.setPassword("*****");
ds.setServerName("localhost");
ds.setPortNumber(1433);
ds.setDatabaseName("AdventureWorks");
XAConnection xaCon = ds.getXAConnection();
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);
// Cleanup.
pstmt.close();
con.close();
xaCon.close();
// Open a new connection and read back the record to verify that it worked.
con = DriverManager.getConnection(connectionUrl);
ResultSet rs = con.createStatement().executeQuery("SELECT * FROM XAMin");
rs.next();
System.out.println("Read -> xid = " + rs.getString(2));
rs.close();
con.close()
}
// Handle any errors that may have occurred.
catch (Exception e) {
e.printStackTrace();
}
}
}
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);
}
}