Compartir a través de


Descripción de las transacciones XA

El controlador JDBC de Microsoft SQL Server 2005 es compatible con las transacciones distribuidas opcionales de J2EE/JDBC 2.0. Las conexiones JDBC obtenidas de la clase SQLServerXADataSource pueden participar en entornos estándar de procesamiento de transacciones distribuidas como pueden ser los servidores de aplicaciones J2EE.

Las clases para la implementación de las transacciones distribuidas son las siguientes:

Clase Implementa Descripción

com.microsoft.sqlserver.jdbc.SQLServerXADataSource

javax.sql.XADataSource

La fábrica de clases para conexiones distribuidas.

com.microsoft.sqlserver.jdbc.SQLServerXAResource

javax.transaction.xa.XAResource

El adaptador de recursos para el administrador de transacciones.

Nota

Las conexiones de transacciones distribuidas XA se establecen de forma predeterminada en el nivel de aislamiento Read Committed.

Cuando utiliza las transacciones XA con SQL Server a través del controlador JDBC de Microsoft SQL Server 2005, puede observar que el rendimiento disminuye. Este problema sólo se produce si el servidor SQL Server que participa en la transacción XA se ejecuta en Windows XP. Por otro lado, las aplicaciones cliente que se ejecutan en Windows XP y se conectan a un servidor SQL Server remoto que no se ejecuta en Windows XP pueden participar en las transacciones XA. Para obtener más información acerca de cómo resolver este problema, vea la revisión que se proporciona en el documento Performance and SQL Server.

Cuando utiliza transacciones XA junto con el Coordinador de transacciones distribuidas de Microsoft (MS DTC, Microsoft Distributed Transaction Coordinator) en Windows Server 2003, puede observar que el método XAResource.setTransactionTimeout no funciona. Para resolver este problema, debe aplicar una revisión que se proporciona en MSDTC and XA Transactions en cada equipo SQL Server que participe en las transacciones XA. Sin esta revisión, el único valor de tiempo de espera válido es el predeterminado de cero (0), que significa que el tiempo de espera es infinito.

Cuando utiliza transacciones XA junto con MS DTC, puede observar que la versión actual de MS DTC no admite el comportamiento de bifurcación XA estrechamente acoplado. Por ejemplo, MS DTC tiene una asignación unívoca entre un identificación de transacción de bifurcación XA (XID) y un identificador de transacción de MS DTC, y el trabajo que realizan las bifurcaciones XA débilmente acopladas se aísla entre sí.

La revisión que se proporciona en MSDTC and Tightly Coupled Transactions habilitan la compatibilidad con las bifurcaciones XA estrechamente acopladas en las que varias bifurcaciones XA con el mismo identificador de transacción global (GTRID) se asignan a una única transacción MS DTC. Esta compatibilidad habilita varias bifurcaciones XA estrechamente acopladas para comprobar los cambios respectivos en el administrador de recursos, como SQL Server.

El controlador JDBC de Microsoft SQL Server 2005 versión 1.2 proporciona un marcador SSTRANSTIGHTLYCPLD para permitir que las aplicaciones usen transacciones XA estrechamente acopladas, que tienen identificadores de transacción de bifurcación XA (XID) diferentes pero el mismo identificador de transacción global (GTRID). Para utilizar esa característica, debe establecer SSTRANSTIGHTLYCPLD en el parámetro flags del método XAResource.start:

xaRes.start(xid, SQLServerXAResource.SSTRANSTIGHTLYCPLD);

Instrucciones de configuración

Los siguientes pasos son necesarios si desea usar los orígenes de datos XA junto con MS DTC para administrar las transacciones distribuidas.

Nota

Los componentes de transacciones distribuidas de JDBC están incluidos en el directorio xa de la instalación del controlador JDBC. Estos componentes incluyen los archivos xa_install.sql y sqljdbc_xa.dll.

Ejecutar el servicio MS DTC

El servicio MS DTC deberá estar marcado como Automático en el Administrador de servicios para garantizar que se esté ejecutando cuando se inicie el servicio de SQL Server. Para habilitar MS DTC para las transacciones XA, debe seguir estos pasos:

  1. En el Panel de control, abra Herramientas administrativas y, a continuación, abra Servicios de componente.

  2. Expanda Servicios de componente, haga clic con el botón secundario del mouse en Mi PC y seleccione Propiedades.

  3. Haga clic en la ficha MSDTC y, a continuación, haga clic en Configuración de seguridad.

  4. Seleccione la casilla Habilitar transacciones XA y haga clic en Aceptar. De este modo, se reinicia el servicio MS DTC.

  5. Vuelva a hacer clic en Aceptar para cerrar el cuadro de diálogo Propiedades y, a continuación, cierre los Servicios de componente.

  6. Detenga y reinicie SQL Server para asegurarse de que se sincroniza con los cambios realizados en MS DTC.

Configurar los componentes de transacciones distribuidas de JDBC

Puede configurar los componentes de transacciones distribuidas del controlador JDBC mediante estos pasos:

  1. Copie sqljdbc_xa.dll del directorio de instalación de JDBC al directorio Binn de cada equipo con SQL Server que vaya a participar en las transacciones distribuidas.

    Nota

    Si usa transacciones XA con un servidor SQL Server de 32 bits, utilice el archivo sqljdbc_xa.dll de la carpeta de x86, aun cuando SQL Server esté instalado en un procesador x64. Si usa transacciones XA con un servidor SQL Server de 64 bits en el procesador x64, utilice el archivo sqljdbc_xa.dll de la carpeta x64. Si usa transacciones XA con un servidor SQL Server de 64 bits en el procesador IA-64, utilice el archivo sqljdbc_xa.dll de la carpeta IA64.

  2. Ejecute el script de base de datos xa_install.sql en cada instancia de SQL Server que participe en las transacciones distribuidas. Este script instala sqljdbc_xa.dll como un procedimiento almacenado extendido. Debe ejecutar este script como administrador de la instancia de SQL Server.

  3. Para conceder permisos a un usuario concreto de modo que pueda participar en las transacciones distribuidas con el controlador JDBC, agregue el usuario a la función SqlJDBCXAUser.

Configurar las funciones definidas por el usuario

Para conceder permisos a un usuario concreto de modo que pueda participar en las transacciones distribuidas con el controlador JDBC, agregue el usuario a la función SqlJDBCXAUser. Por ejemplo, use el siguiente código Transact-SQL para agregar un usuario llamado 'shelby' (usuario de inicio de sesión estándar de SQL llamado 'shelby') a la función SqlJDBCXAUser:

USE master
GO
EXEC sp_grantdbaccess 'shelby', 'shelby'
GO
EXEC sp_addrolemember [SqlJDBCXAUser], 'shelby'

Las funciones definidas por el usuario de SQL se definen para cada base de datos. Para crear su propia función por motivos de seguridad, debe definir la función en cada base de datos y agregar los usuarios para cada base de datos. La función SqlJDBCXAUser se define estrictamente en la base de datos maestra porque se utiliza para conceder acceso a los procedimientos almacenados extendidos del controlador JDBC de SQL que se encuentran en la base de datos maestra. En primer lugar, debe conceder acceso a cada usuario a la base de datos maestra y, a continuación, concederles acceso a la función SqlJDBCXAUser mientras esté conectado a la base de datos maestra.

Ejemplo

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);
   }
}

Vea también

Otros recursos

Realizar transacciones con el controlador JDBC