Transactions XA

Le pilote JDBC Microsoft SQL Server prend en charge les transactions distribuées facultatives Java Platform, Enterprise Edition 5/JDBC 2.0. Les connexions JDBC obtenues à partir de la classe SQLServerXADataSource peuvent participer aux environnements de traitement des transactions distribuées standard, tels que les serveurs d'applications Java Platform, Enterprise Edition 5 (Java EE 5).

Les classes pour l'implémentation des transactions distribuées sont les suivantes :

Classe Implémentation Description

com.microsoft.sqlserver.jdbc.SQLServerXADataSource

javax.sql.XADataSource

Fabrique de classe pour les connexions distribuées.

com.microsoft.sqlserver.jdbc.SQLServerXAResource

javax.transaction.xa.XAResource

Adaptateur de ressources pour le gestionnaire de transactions.

RemarqueRemarque :

Les connexions de transactions distribuées XA sont définies par défaut au niveau d'isolation Read Committed.

Recommandations et limitations relatives à l'utilisation de transactions XA

La liste suivante fournit des informations sur les limitations et recommandations spécifiques à l'utilisation de transactions XA :

  • Sur Windows XP :

    Lorsque vous utilisez des transactions XA avec SQL Server via le pilote JDBC Microsoft SQL Server, vous pouvez remarquer un éventuel non-fonctionnement des transactions XA. Ce problème s'applique seulement lorsque le serveur SQL Server qui participe à la transaction XA s'exécute sur Windows XP. En revanche, les applications clientes qui s'exécutent sur Windows XP et qui se connectent à un SQL Server distant qui ne s'exécute pas sur Windows XP peuvent participer aux transactions XA. Pour plus d'informations sur la résolution de ce problème, consultez les détails relatifs au correctif logiciel disponible sur la page Web Windows XP and XA Transactions (en anglais).

  • Sur Windows Server 2003 :

    Lorsque vous utilisez des transactions XA avec MS DTC (Microsoft Distributed Transaction Coordinator) sur Windows Server 2003, vous pouvez remarquer que la méthode XAResource.setTransactionTimeout ne fonctionne pas. Pour résoudre ce problème, vous devez appliquer un correctif logiciel disponible sur la page Web MSDTC and XA Transactions (en anglais) à chaque ordinateur SQL Server qui participe aux transactions XA. Sans ce correctif, la seule valeur de délai d'attente valide est la valeur par défaut, à savoir 0, ce qui signifie que le délai d'attente est infini.

Les recommandations supplémentaires suivantes s'appliquent aux transactions fortement couplées :

  • Lorsque vous utilisez des transactions XA avec MS DTC (Microsoft Distributed Transaction Coordinator), vous pouvez remarquer que la version actuelle de MS DTC ne prend pas en charge le comportement de branche XA fortement couplée. Par exemple, MS DTC a un mappage un-à-un entre un ID de transaction de branche XA (XID) et un ID de transaction MS DTC et le travail effectué par les branches XA couplées de manière lâche est isolé.

    Le correctif logiciel disponible sur la page Web MSDTC and Tightly Coupled Transactions (en anglais) autorise la prise en charge des branches XA étroitement couplées lorsque plusieurs branches XA avec le même ID de transaction globale (GTRID) sont mappées à un ID de transaction MS DTC unique. Cette prise en charge permet à plusieurs branches XA fortement couplées de voir les modifications apportées à chacune d'elles dans le gestionnaire de ressources, par exemple SQL Server.

  • À partir de la version 1.2 du pilote JDBC Microsoft SQL Server 2005, un indicateur SSTRANSTIGHTLYCPLD est introduit pour permettre aux applications d'utiliser les transactions XA fortement couplées qui ont des ID de transactions de branche XA (XID) différents mais le même ID de transaction global (GTRID). Pour pouvoir utiliser cette fonctionnalité, vous devez définir le SSTRANSTIGHTLYCPLD sur le paramètre flags de la méthode XAResource.start :

    xaRes.start(xid, SQLServerXAResource.SSTRANSTIGHTLYCPLD);
    

Instructions relatives à la configuration

Les étapes suivantes sont requises si vous souhaitez utiliser des sources de données XA avec Microsoft Distributed Transaction Coordinator (MS DTC) pour manipuler des transactions distribuées.

RemarqueRemarque :

Les composants de transaction distribuée JDBC sont inclus dans le répertoire xa de l'installation du pilote JDBC. Ces composants incluent les fichiers xa_install.sql et sqljdbc_xa.dll.

Exécution du service MS DTC

Le service MS DTC doit être marqué comme Automatique dans le gestionnaire de services afin de garantir son exécution si le service SQL Server démarre. Pour activer MS DTC pour les transactions XA, vous devez procéder comme suit :

Sur Windows XP et Windows Server 2003 :

  1. Dans le Panneau de configuration, ouvrez Outils d'administration, puis Services de composants. Vous pouvez également cliquer sur le bouton Démarrer, sur Exécuter, taper dcomcnfg dans la zone Ouvrir, puis cliquer sur OK pour ouvrir Services de composants.

  2. Développez Services de composants, Ordinateurs, puis cliquez avec le bouton droit sur Poste de travail et sélectionnez Propriétés.

  3. Cliquez sur l'onglet MSDTC, puis sur Configuration de la sécurité.

  4. Activez la case à cocher Transactions XA, puis cliquez sur OK. Ceci entraînera le redémarrage du service MS DTC.

  5. Cliquez à nouveau sur OK pour fermer la boîte de dialogue Propriétés, puis fermez Services de composants.

  6. Arrêtez, puis redémarrez SQL Server afin de garantir sa synchronisation avec les modifications de MS DTC.

Sur Windows Vista :

  1. Cliquez sur le bouton Démarrer, tapez dcomcnfg dans la zone Rechercher, puis appuyez sur ENTRÉE pour ouvrir Services de composants. Vous pouvez également taper %windir%\system32\comexp.msc dans la zone Rechercher du menu Démarrer pour ouvrir Services de composants.

  2. Développez Services de composants, Ordinateurs, Poste de travail, puis Coordinateur de transactions distribuées.

  3. Cliquez avec le bouton droit sur DTC local, puis sélectionnez Propriétés.

  4. Dans la boîte de dialogue Propriétés du DTC local, cliquez sur l'onglet Sécurité.

  5. Activez la case à cocher Activer les transactions XA, puis cliquez sur OK. Ceci entraînera le redémarrage du service MS DTC.

  6. Cliquez à nouveau sur OK pour fermer la boîte de dialogue Propriétés, puis fermez Services de composants.

  7. Arrêtez, puis redémarrez SQL Server afin de garantir sa synchronisation avec les modifications de MS DTC.

Configuration des composants de transaction distribuée JDBC

Vous pouvez configurer les composants de transaction distribuée du pilote JDBC en suivant les étapes suivantes :

  1. Copiez le fichier sqljdbc_xa.dll depuis le répertoire d'installation JDBC vers le répertoire Binn de chaque ordinateur SQL Server susceptible de participer à des transactions distribuées.

    RemarqueRemarque :

    Si vous utilisez des transactions XA avec un SQL Server 32 bits, utilisez le fichier sqljdbc_xa.dll dans le dossier x86, même si le SQL Server est installé sur un processeur x64. Si vous utilisez des transactions XA avec une version 64 bits de SQL Server sur un processeur x64, utilisez le fichier sqljdbc_xa.dll dans le dossier x64. Si vous utilisez des transactions XA avec une version 64 bits de SQL Server sur un processeur Itanium, utilisez le fichier sqljdbc_xa.dll dans le dossier IA64.

  2. Exécutez le script de base de données xa_install.sql sur chaque instance de SQL Server susceptible de participer à des transactions distribuées. Ce script installe les procédures stockées étendues qui sont appelées par sqljdbc_xa.dll. Ces procédures stockées étendues implémentent la prise en charge des transactions distribuées et de XA pour le pilote JDBC 2.0 Microsoft SQL Server. Vous devez exécuter ce script en tant qu'administrateur de l'instance de SQL Server.

  3. Pour autoriser un utilisateur spécifique à participer à des transactions distribuées avec le pilote JDBC, ajoutez-le au rôle SqlJDBCXAUser.

Vous ne pouvez configurer qu'une seule version à la fois de l'assembly sqljdbc_xa.dll sur chaque instance de SQL Server. Les applications devront peut-être utiliser des versions différentes du pilote JDBC pour se connecter à la même instance de SQL Server via la connexion XA. Dans ce cas, sqljdbc_xa.dll, qui est livré avec le pilote JDBC le plus récent, doit être installé sur l'instance de SQL Server.

Il existe trois façons de vérifier la version actuellement installée de sqljdbc_xa.dll sur l'instance de SQL Server :

  1. Ouvrez le répertoire LOG de l'ordinateur SQL Server qui doit participer aux transactions distribuées. Sélectionnez et ouvrez le fichier ERRORLOG SQL Server. Recherchez l'expression « Using 'SQLJDBC_XA.dll' version ... » dans le fichier ERRORLOG.

  2. Ouvrez le répertoire Binn de l'ordinateur SQL Server qui doit participer aux transactions distribuées. Sélectionnez l'assembly sqljdbc_xa.dll.

    1. Sur Windows Vista : Cliquez avec le bouton droit sur sqljdbc_xa.dll, puis sélectionnez Propriétés. Cliquez ensuite sur l'onglet Détails. Le champ Version du fichier indique la version de sqljdbc_xa.dll actuellement installée sur l'instance de SQL Server.

    2. Sur Windows XP et Windows Server 2003 : Cliquez avec le bouton droit sur sqljdbc_xa.dll, puis sélectionnez Propriétés. Cliquez ensuite sur l'onglet Version. Le champ Version du fichier indique la version de sqljdbc_xa.dll actuellement installée sur l'instance de SQL Server.

  3. Définissez la fonctionnalité de journalisation comme indiqué dans l'exemple de code de la prochaine section. Recherchez l'expression « Server XA DLL version:... » dans le fichier journal de sortie.

Configuration des rôles définis par l'utilisateur

Pour autoriser un utilisateur spécifique à participer à des transactions distribuées avec le pilote JDBC, ajoutez-le au rôle SqlJDBCXAUser. Par exemple, utilisez le code Transact-SQL suivant pour ajouter un utilisateur appelé « shelby » (« shelby » est un nom d'utilisateur standard d'ouverture de session SQL) au rôle SqlJDBCXAUser :

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

Les rôles définis par l'utilisateur SQL sont définis par base de données. Pour des raisons de sécurité, si vous souhaitez créer votre propre rôle, vous devez définir le rôle dans chaque base de données et ajouter les utilisateurs par base de données. Le rôle SqlJDBCXAUser est strictement défini dans la base de données maîtresse car il est utilisé pour accorder l'accès aux procédures stockées étendues SQL JDBC se trouvant dans la base de données maîtresse. Vous devrez d'abord accorder un accès à la base de données maîtresse à l'utilisateur individuel, puis lui accorder un accès au rôle SqlJDBCXAUser en étant connecté à la base de données maîtresse.

Exemple

import java.net.Inet4Address;
import java.sql.*;
import java.util.Random;
import javax.transaction.xa.*;
import javax.sql.*;
import com.microsoft.sqlserver.jdbc.*;
import java.util.logging.*;

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 a logger.
         Logger logger = Logger.getLogger("com.microsoft.sqlserver.jdbc.internals.XA");
         FileHandler fh = new FileHandler("outputLog.txt");
         logger.addHandler(fh);
         logger.setLevel(Level.FINE);

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

Voir aussi

Autres ressources

Exécution de transactions avec le pilote JDBC