El Controlador Microsoft JDBC para SQL Server admite compatibilidad con las transacciones distribuidas opcionales de Java Platform, Enterprise Edition/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 de Java Platform, Enterprise Edition (Java EE).

Notas

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.

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

Instrucciones y limitaciones cuando se usan transacciones XA

Las siguientes instrucciones adicionales se aplican a las transacciones fuertemente acopladas:

  • Cuando utiliza transacciones XA junto con MS DTC, puede observar que la versión actual de Microsoft DTC (Coordinador de transacciones distribuidas) no admite el comportamiento de bifurcación XA estrechamente acoplado. Por ejemplo, MS DTC tiene una asignación unívoca entre un identificador de rama de transacció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 estas.

    La revisión que se proporciona en MSDTC and Tightly Coupled Transactions habilita 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 un único identificador de transacción de MS DTC. Esta compatibilidad habilita varias bifurcaciones XA fuertemente acopladas para comprobar los cambios respectivos en el administrador de recursos, como SQL Server.

  • Una marca SSTRANSTIGHTLYCPLD permite que las aplicaciones usen transacciones XA estrechamente acopladas, que tienen identificadores de rama de transacción XA diferentes (BQUAL), pero el mismo identificador de transacción global (GTRID, Global Transaction ID) e identificador de formato (FormatID). 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 Microsoft DTC (Coordinador de transacciones distribuidas) para administrar las transacciones distribuidas.

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 asegurarse de que se esté ejecutando cuando se inicie el servicio de SQL Server. Para habilitar Microsoft DTC (Coordinador de transacciones distribuidas) para las transacciones XA, debe seguir estos pasos:

En Windows Vista y versiones posteriores:

  1. Haga clic en Inicio, escriba dcomcnfg en el cuadro Iniciar búsqueda y presione ENTRAR para abrir Servicios de componentes. También puede escribir %windir%\system32\comexp.msc en el cuadro Iniciarbúsqueda para abrir Servicios de componentes.

  2. Expanda Servicios de componentes, Equipos, Mi PC y Microsoft DTC (Coordinador de transacciones distribuidas).

  3. Haga clic con el botón secundario en DTC local y seleccione Propiedades.

  4. En el cuadro de diálogo Propiedades de DTC local, haga clic en la pestaña Seguridad.

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

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

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

Configurar los componentes de transacciones distribuidas de JDBC

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

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

    Si usa transacciones XA con un servidor SQL Server de 32 bits, utilice el archivo sqljdbc_xa.dll de la carpeta 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.

  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 los procedimientos almacenados extendidos que son llamados por sqljdbc_xa.dll. Estos procedimientos almacenados extendidos implementan compatibilidad con transacciones distribuidas y XA para el Controlador Microsoft JDBC para SQL Server. 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 al rol SqlJDBCXAUser.

De manera simultánea solamente puede configurar una versión del ensamblado sqljdbc_xa.dll en cada instancia de SQL Server. Las aplicaciones pueden necesitar usar diferentes versiones del controlador JDBC para conectar con la misma instancia de SQL Server usando la conexión XA. En ese caso, sqljdbc_xa.dll, que se incluye con el controlador JDBC más reciente, debe instalarse en la instancia de SQL Server.

Hay tres maneras de comprobar qué versión de sqljdbc_xa.dll está actualmente instalada en la instancia de SQL Server:

  1. Abra el directorio LOG del equipo con SQL Server que participará en las transacciones distribuidas. Seleccione y abra el archivo "ERRORLOG" de SQL Server. Busque la frase "Using 'SQLJDBC_XA.dll' version ..." en el archivo "ERRORLOG".

  2. Abra el directorio Binn del equipo con SQL Server que participará en las transacciones distribuidas. Seleccione el ensamblado sqljdbc_xa.dll.

    • En Windows Vista o versiones posteriores, haga clic con el botón secundario en sqljdbc_xa.dll y, a continuación, seleccione Propiedades. Después, haga clic en la pestaña Detalles. El campo Versión del archivo muestra qué versión de sqljdbc_xa.dll está actualmente instalada en la instancia de SQL Server.

  3. Configure la funcionalidad de registro tal como se muestra en el ejemplo de código de la sección siguiente. Busque la frase "Server XA DLL version:..." en el archivo de registro de resultados.

Actualización de sqljdbc_xa.dll

Al instalar una nueva versión del controlador JDBC, también debe usar sqljdbc_xa.dll de la nueva versión para actualizar sqljdbc_xa.dll en el servidor.

Debe actualizar sqljdbc_xa.dll durante un intervalo de mantenimiento o cuando no haya transacciones MS DTC en curso.

  1. Descargue sqljdbc_xa.dll con el comando DBCC sqljdbc_xa (FREE) de Transact-SQL.

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

    La nueva DLL se cargará cuando se llame a un procedimiento extendido en sqljdbc_xa.dll. No necesita reiniciar SQL Server para cargar las nuevas definiciones.

Configurar los roles definidos 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 al rol SqlJDBCXAUser. Por ejemplo, use el siguiente código Transact-SQL para agregar un usuario denominado 'shelby' (usuario de inicio de sesión estándar de SQL denominado 'shelby') al rol SqlJDBCXAUser:

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

Los roles definidos por el usuario de SQL se definen para cada base de datos. Para crear su propio rol por motivos de seguridad, debe definir el rol en cada base de datos y agregar los usuarios para cada base de datos. El rol 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 al rol 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 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);

         // 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.
         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