Microsoft SQL Server 2005 JDBC Driver 提供对 J2EE/JDBC 2.0 可选分布式事务的支持。从 SQLServerXADataSource 类获取的 JDBC 连接可以参与标准分布式事务处理环境,例如 J2EE 应用程序服务器。
用于此分布式事务实现的类如下:
| 类 | 实现 | 说明 |
|---|---|---|
|
com.microsoft.sqlserver.jdbc.SQLServerXADataSource |
javax.sql.XADataSource |
分布式连接的类工厂。 |
|
com.microsoft.sqlserver.jdbc.SQLServerXAResource |
javax.transaction.xa.XAResource |
事务管理器的资源适配器。 |
当您通过 Microsoft SQL Server 2005 JDBC Driver 将 XA 事务用于 SQL Server 时,您可能会注意到性能降低了。仅当正参与 XA 事务的 SQL Server 正在 Windows XP 上运行时,才会出现此问题。另一方面,在 Windows XP 上运行且与不在 Windows XP 上运行的远程 SQL Server 相连的客户端应用程序可以参与 XA 事务。有关如何解决此问题的详细信息,请参阅
当您在 Windows Server 2003 上将 XA 事务与 Microsoft 分布式事务处理协调器 (MS DTC) 一起使用时,您可能会注意到 XAResource.setTransactionTimeout 方法不起作用。若要解决此问题,必须将
当您将 XA 事务与 Microsoft 分布式事务处理协调器 (MS DTC) 一起使用时,您可能会注意到 MS DTC 的当前版本不支持紧密结合的 XA 分支行为。例如,MS DTC 在 XA 分支事务 ID (XID) 与 MS DTC 事务 ID 之间具有一对一的映射,由松散耦合的 XA 分支执行的工作彼此之间是隔离的。
借助于在
Microsoft SQL Server 2005 JDBC Driver 版本 1.2 提供了一个 SSTRANSTIGHTLYCPLD 标志以允许应用程序使用紧密结合的 XA 事务,这些事务具有不同的 XA 分支事务 ID (XID),但具有相同的全局事务 ID (GTRID)。为了使用该功能,必须对于 XAResource.start 方法的 flags 参数设置 SSTRANSTIGHTLYCPLD:
xaRes.start(xid, SQLServerXAResource.SSTRANSTIGHTLYCPLD);
配置说明
如果要同时使用 XA 数据源和 Microsoft 分布式事务处理协调器 (MS DTC) 来处理分布式事务,则需要执行以下步骤。
运行 MS DTC 服务
在服务管理器中,MS DTC 服务应标记为“自动”,以确保其在启动 SQL Server 服务时运行。若要为 XA 事务启用 MS DTC,必须执行以下步骤:
-
在“控制面板”中,打开“管理工具”,然后打开“组件服务”。
-
展开“组件服务”,右键单击“我的电脑”,然后选择“属性”。
-
单击“MSDTC”选项卡,再单击“安全配置”。
-
选中“启用 XA 事务”复选框,然后单击“确定”。这将使 MS DTC 服务重新启动。
-
再次单击“确定”以关闭“属性”对话框,然后关闭“组件服务”。
-
停止 SQL Server,然后重新启动,以确保它与 MS DTC 更改同步。
配置 JDBC 分布式事务组件
可通过以下步骤配置 JDBC 驱动程序分布式事务组件:
-
将 sqljdbc_xa.dll 从 JDBC 安装目录复制到每台要参与分布式事务的 SQL Server 计算机的 Binn 目录中。
注意: 如果您将 XA 事务用于 32 位 SQL Server,则使用 x86 文件夹中的 sqljdbc_xa.dll 文件,即使 SQL Server 安装在 x64 处理器上也不例外。如果您在 x64 处理器上将 XA 事务用于 64 位 SQL Server,则使用 x64 文件夹中的 sqljdbc_xa.dll 文件。如果您在 IA-64 处理器上将 XA 事务用于 64 位 SQL Server,则使用 IA-64 文件夹中的 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 角色的定义非常严格,因为该角色用于授予对主数据库中驻留的 SQL JDBC 扩展存储过程的访问权限。登录到主数据库后,必须先授予单个用户对主数据库的访问权限,然后再授予这些用户对 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);
}
}