了解 XA 事务

了解 XA 事务

SQL Server 2008 R2

Microsoft SQL Server JDBC 提供程序提供对 Java Platform, Enterprise Edition/JDBC 2.0 可选分布式事务的支持。从 SQLServerXADataSource 类获取的 JDBC 连接可以参与标准分布式事务处理环境,例如 Java Platform, Enterprise Edition (Java EE) 应用程序服务器。

用于此分布式事务实现的类如下:

实现说明

com.microsoft.sqlserver.jdbc.SQLServerXADataSource

javax.sql.XADataSource

分布式连接的类工厂。

com.microsoft.sqlserver.jdbc.SQLServerXAResource

javax.transaction.xa.XAResource

事务管理器的资源适配器。

Aa342335.note(zh-cn,SQL.105).gif注意:
XA 分布式事务连接默认使用“提交读”隔离级别。

下面的列表提供了有关使用 XA 事务时的具体限制和准则的信息:

  • 在 Windows XP 上:
    当您通过 Microsoft SQL Server JDBC Driver 将 XA 事务用于 SQL Server 时,可能会注意到 XA 事务不起作用。仅当正参与 XA 事务的 SQL Server 正在 Windows XP 上运行时,才会出现此问题。另一方面,在 Windows XP 上运行且与不在 Windows XP 上运行的远程 SQL Server 相连的客户端应用程序可以参与 XA 事务。有关如何解决此问题的详细信息,请参阅 Windows XP COM+ 修复程序总成包 14 的可用性中提供的修补程序。
  • 在 Windows Server 2003 上:
    当您在 Windows Server 2003 上将 XA 事务与 Microsoft 分布式事务处理协调器 (MS DTC) 一起使用时,您可能会注意到 XAResource.setTransactionTimeout 方法不起作用。若要解决此问题,必须将 MSDTC 和 XA 事务中提供的一个修补程序应用于正在参与 XA 事务的每一台 SQL Server 计算机。如果不应用此修补程序,则唯一有效的超时值为默认值 0,这意味着无限超时。

以下附加准则适用于紧密耦合的事务:

  • 当您将 XA 事务与 Microsoft 分布式事务处理协调器 (MS DTC) 一起使用时,您可能会注意到 MS DTC 的当前版本不支持紧密结合的 XA 分支行为。例如,MS DTC 在 XA 分支事务 ID (XID) 与 MS DTC 事务 ID 之间具有一对一的映射,由松散耦合的 XA 分支执行的工作彼此之间是隔离的。
    借助于在 MSDTC 和紧密结合的事务中提供的修补程序,可以支持紧密结合的 XA 分支,其中,多个具有相同全局事务 ID (GTRID) 的 XA 分支映射到单一 MS DTC 事务 ID。这种支持使得多个紧密结合的 XA 分支可以在资源管理器(如 SQL Server)中看到彼此发生的变化。
  • SSTRANSTIGHTLYCPLD 标志允许应用程序使用紧密结合的 XA 事务,这些事务具有不同的 XA 分支事务 ID (XID),但具有相同的全局事务 ID (GTRID)。为了使用该功能,必须对 XAResource.start 方法的 flags 参数设置 SSTRANSTIGHTLYCPLD
    xaRes.start(xid, SQLServerXAResource.SSTRANSTIGHTLYCPLD);
    

如果要同时使用 XA 数据源和 Microsoft 分布式事务处理协调器 (MS DTC) 来处理分布式事务,则需要执行以下步骤。

Aa342335.note(zh-cn,SQL.105).gif注意:
JDBC 分布式事务组件包含在 JDBC 驱动程序安装的 xa 目录中。这些组件包括 xa_install.sql 和 sqljdbc_xa.dll 文件。

运行 MS DTC 服务

在服务管理器中,MS DTC 服务应标记为“自动”,以确保其在启动 SQL Server 服务时运行。若要为 XA 事务启用 MS DTC,必须执行以下步骤:

在 Windows XP 和 Windows Server 2003 上:

  1. “控制面板”中,打开“管理工具”,然后打开“组件服务”。也可以单击“开始”按钮,单击“运行”,在“打开”框中键入 dcomcnfg,然后按“确定”打开“组件服务”
  2. 展开“组件服务”、“计算机”,右键单击“我的电脑”,然后选择“属性”
  3. 单击“MSDTC”选项卡,再单击“安全性配置”
  4. 选中“启用 XA 事务”复选框,然后单击“确定”。这将使 MS DTC 服务重新启动。
  5. 再次单击“确定”以关闭“属性”对话框,然后关闭“组件服务”。
  6. 停止 SQL Server,然后重新启动,以确保它与 MS DTC 更改同步。

在 Windows Vista 上:

  1. 单击“开始”按钮,在“开始搜索”框中键入 dcomcnfg,然后按 Enter 打开“组件服务”。也可以在“开始搜索”框中键入 %windir%\system32\comexp.msc 打开“组件服务”
  2. 依次展开“组件服务”、“计算机”、“我的电脑”和“分布式事务处理协调器”。
  3. 右键单击“本地 DTC”,再选择“属性”
  4. 单击“本地 DTC 属性”对话框上的“安全”选项卡。
  5. 选中“启用 XA 事务”复选框,然后单击“确定”。这将使 MS DTC 服务重新启动。
  6. 再次单击“确定”以关闭“属性”对话框,然后关闭“组件服务”
  7. 停止 SQL Server,然后重新启动,确保它与 MS DTC 更改同步。

配置 JDBC 分布式事务组件

可通过以下步骤配置 JDBC 驱动程序分布式事务组件:

  1. 将 sqljdbc_xa.dll 从 JDBC 安装目录复制到每台要参与分布式事务的 SQL Server 计算机的 Binn 目录中。
    Aa342335.note(zh-cn,SQL.105).gif注意:
    如果您将 XA 事务用于 32 位 SQL Server,则使用 x86 文件夹中的 sqljdbc_xa.dll 文件,即使 SQL Server 安装在 x64 处理器上也不例外。如果您在 x64 处理器上将 XA 事务用于 64 位 SQL Server,则使用 x64 文件夹中的 sqljdbc_xa.dll 文件。如果您是在 Itanium 处理器上将 XA 事务用于 64 位 SQL Server,则使用 IA64 文件夹中的 sqljdbc_xa.dll 文件。
  2. 对每个要参与分布式事务的 SQL Server 实例执行数据库脚本 xa_install.sql。此脚本将安装 sqljdbc_xa.dll 调用的扩展存储过程。这些扩展存储过程为 Microsoft SQL Server JDBC Driver 实现分布式事务和 XA 支持。需要以 SQL Server 实例管理员的身份来运行此脚本。
  3. 若要为特定用户授予使用 JDBC Driver 参与分布式事务的权限,请将该用户添加至 SqlJDBCXAUser 角色。

在每个 SQL Server 实例上,一次只能配置一个版本的 sqljdbc_xa.dll 程序集。应用程序可能需要使用不同版本的 JDBC driver,才能使用 XA 连接来连接到同一个 SQL Server 实例。在这种情况下,必须在 SQL Server 实例上安装最新的 JDBC driver 附带的 sqljdbc_xa.dll。

可以通过三种方法来验证 SQL Server 实例上当前安装的 sqljdbc_xa.dll 的版本:

  1. 打开将参与分布式事务处理的 SQL Server 计算机的 LOG 目录。选择并打开 SQL Server 的“ERRORLOG”文件。在“ERRORLOG”文件中搜索“使用‘SQLJDBC_XA.dll’版本 ...”这一短语。
  2. 打开将参与分布式事务处理的 SQL Server 计算机的 Binn 目录。选择 sqljdbc_xa.dll 程序集。
    1. 在 Windows Vista 上:右键单击 sqljdbc_xa.dll,然后选择“属性”。然后单击“详细信息”选项卡。“文件版本”字段显示 SQL Server 实例上当前安装的 sqljdbc_xa.dll 的版本。
    2. 在 Windows XP 和 Windows 2003 Server 上:右键单击 sqljdbc_xa.dll,然后选择“属性”。接下来,单击“版本”选项卡。“文件版本”字段显示 SQL Server 实例上当前安装的 sqljdbc_xa.dll 的版本。
  3. 按照下一节的代码示例所示设置日志记录功能。在输入日志文件中搜索“服务器 XA DLL 版本:...”这一短语。

配置用户定义的角色

若要为特定用户授予使用 JDBC Driver 参与分布式事务的权限,请将该用户添加至 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 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
         + ";databaseName=" + databaseName + ";user=" + user + ";password=" + 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(user);
         ds.setPassword(password);
         ds.setServerName(serverName);
         ds.setPortNumber(portNumber);
         ds.setDatabaseName(databaseName);
         XAConnection xaCon = ds.getXAConnection();
         con = xaCon.getConnection();

         // Get a unique Xid object for testing.
         XAResource xaRes = null;
         Xid xid = null;
         xid = XidImpl.getUniqueXid(1);

         // Cleanup.
         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);
   }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值