Grundlegendes zu XA-Transaktionen

Microsoft SQL Server 2005 JDBC Driver unterstützt optional verteilte J2EE-/JDBC 2.0-Transaktionen. JDBC-Verbindungen von der SQLServerXADataSource-Klasse können Bestandteil normaler verteilter Transaktionsverarbeitungsumgebungen wie J2EE-Anwendungsservern sein.

Für die Implementierung verteilter Transaktionen stehen die folgenden Klassen zur Verfügung:

Klasse Implementiert Beschreibung

com.microsoft.sqlserver.jdbc.SQLServerXADataSource

javax.sql.XADataSource

Das Klassenfactory für verteilte Verbindungen.

com.microsoft.sqlserver.jdbc.SQLServerXAResource

javax.transaction.xa.XAResource

Der Ressourcenadapter für den Transaktions-Manager.

Hinweis

Verbindungen für verteilte XA-Transaktionen verwenden standardmäßig die Isolationsstufe "Lesen mit Commit".

Wenn Sie XA-Transaktionen mit SQL Server über den Microsoft SQL Server 2005-JDBC-Treiber verwenden, können Leistungsverringerungen auftreten. Dieses Problem tritt nur auf, wenn der SQL Server, der an der XA-Transaktion teilnimmt, unter Windows XP ausgeführt wird. Allerdings können die Clientanwendungen unter Windows XP, die eine Verbindung mit einem Remote-SQL Server herstellen, der nicht unter Windows XP ausgeführt wird, an XA-Transaktionen teilnehmen. Weitere Informationen zum Beheben dieses Problems finden Sie im Hotfix unter Leistung und SQL Server.

Wenn Sie XA-Transaktionen zusammen mit Microsoft Distributed Transaction Coordinator (MS DTC) unter Windows Server 2003 verwenden, bemerken Sie möglicherweise, dass die XAResource.setTransactionTimeout-Methode nicht funktioniert. Zum Beheben dieses Problems müssen Sie den unter MSDTC und XA- Transaktionen bereitgestellten Hotfix auf alle an XA-Transaktionen beteiligten SQL Server-Computer anwenden. Ohne diese Korrektur ist nur der Standardwert von 0 als Timeoutwert gültig, d. h. ein unendliches Timeout.

Wenn Sie XA-Transaktionen zusammen mit Microsoft Distributed Transaction Coordinator (MS DTC) verwenden, bemerken Sie möglicherweise, dass die aktuelle Version von MS DTC eng verkoppeltes XA-Verzweigungsverhalten nicht unterstützt. MS DTC verfügt beispielsweise über eine 1:1-Zuordnung zwischen einer Transaktions-ID für eine XA-Verzweigung (XID) und einer MS DTC-Transaktions-ID, und die von lose verbundenen XA-Verzweigungen ausgeführten Aktionen sind voneinander isoliert.

Der unter MSDTC und eng verkoppelte Transaktionen bereitgestellte Hotfix ermöglicht die Unterstützung von eng verkoppelten XA-Verzweigungen, wobei mehrere XA-Verzweigungen mit derselben globalen Transaktions-ID (GTRID) einer einzelnen MS DTC-Transaktions-ID zugeordnet werden. Durch diese Unterstützung können mehrere eng verkoppelte XA-Verzweigungen die jeweiligen Änderungen im Ressourcenmanager anzeigen, beispielsweise SQL Server.

Der Microsoft SQL Server 2005-JDBC-Treiber Version 1.2 stellt ein SSTRANSTIGHTLYCPLD-Flag bereit, damit die Anwendungen die eng verkoppelten XA-Transaktionen verwenden können, die andere XA-Verzweigungstransaktions-IDs (XIDs) aufweisen, jedoch über die gleiche globale Transaktions-ID (GTRID) verfügen. Damit Sie dieses Feature verwenden können, müssen Sie SSTRANSTIGHTLYCPLD im flags-Parameter der XAResource.start-Methode festlegen:

xaRes.start(xid, SQLServerXAResource.SSTRANSTIGHTLYCPLD);

Konfigurationsanweisungen

Die folgenden Schritte sind erforderlich, wenn Sie XA-Datenquellen zusammen mit Microsoft Distributed Transaction Coordinator (MS DTC) verwenden möchten.

Hinweis

Die JDBC-Komponenten für verteilte Transaktionen sind im Verzeichnis "xa" der JDBC-Treiberinstallation enthalten. Zu diesen Komponenten zählen die Dateien "xa_install.sql" und "sqljdbc_xa.dll".

Ausführen des MS DTC-Diensts

Der MS DTC-Dienst muss im Dienst-Manager als Automatisch markiert werden, damit er beim Starten des SQL Server-Diensts ausgeführt wird. Führen Sie die folgenden Schritte aus, um MS DTC für XA-Transaktionen zu aktivieren:

  1. Klicken Sie in der Systemsteuerung auf Verwaltung, und klicken Sie dann auf Komponentendienste.

  2. Erweitern Sie "Komponentendienste", klicken Sie mit der rechten Maustaste auf Arbeitsplatz, und wählen Sie dann Eigenschaften aus.

  3. Klicken Sie auf die Registerkarte MSDTC und dann auf Sicherheitskonfiguration.

  4. Aktivieren Sie das Kontrollkästchen XA-Transaktionen ermöglichen, und klicken Sie dann auf OK. Daraufhin muss der MS DTC-Dienst neu gestartet werden.

  5. Klicken Sie erneut auf OK, um das Dialogfeld Eigenschaften zu schließen, und schließen Sie dann Komponentendienste.

  6. Beenden und starten Sie SQL Server erneut, damit die MS DTC-Änderungen synchronisiert werden.

Konfigurieren der JDBC-Komponenten für verteilte Transaktionen

Führen Sie die folgenden Schritte aus, um die Komponenten des JDBC-Treibers für verteilte Transaktionen zu konfigurieren:

  1. Kopieren Sie die Datei "sqljdbc_xa.dll" aus dem JDBC-Installationsverzeichnis auf allen SQL Server-Computern, die an verteilten Transaktionen teilnehmen, in das Verzeichnis "Binn".

    Hinweis

    Wenn Sie XA-Transaktionen mit einem 32-Bit-SQL Server verwenden, verwenden Sie die Datei sqljdbc_xa.dll im Ordner x86, auch wenn SQL Server auf einem x64-Prozessor installiert ist. Wenn Sie XA-Transaktionen mit einem 64-Bit-SQL Server auf dem x64-Prozessor verwenden, verwenden Sie die Datei sqljdbc_xa.dll im Ordner x64. Wenn Sie XA-Transaktionen mit einem 64-Bit-SQL Server auf einem IA-64-Prozessor verwenden, verwenden Sie die Datei sqljdbc_xa.dll im Ordner IA64.

  2. Führen Sie auf allen SQL Server-Computern, die an verteilten Transaktionen teilnehmen, das Datenbankskript "xa_install.sql" aus. Durch dieses Skript wird "sqljdbc_xa.dll" als erweiterte gespeicherte Prozedur installiert. Sie müssen dieses Skript als Administrator der SQL Server-Instanz ausführen.

  3. Um einem bestimmten Benutzer die Berechtigungen für die Teilnahme an verteilten Transaktionen mit dem JDBC-Treiber zu gewähren, fügen Sie den Benutzer zu der Rolle "SqlJDBCXAUser" hinzu.

Konfigurieren der benutzerdefinierten Rollen

Um einem bestimmten Benutzer die Berechtigungen für die Teilnahme an verteilten Transaktionen mit dem JDBC-Treiber zu gewähren, fügen Sie den Benutzer zu der Rolle "SqlJDBCXAUser" hinzu. Mit dem folgenden Transact-SQL-Code können Sie beispielsweise einen Benutzer mit dem Namen 'shelby' (Benutzer mit dem Namen 'shelby', der die normale SQL-Anmeldung verwendet) zu der Rolle "SqlJDBCXAUser" hinzufügen:

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

Benutzerdefinierte SQL-Rollen werden jeweils für eine Datenbank definiert. Wenn Sie aus Sicherheitsgründen eigene Rollen erstellen möchten, müssen Sie die Rolle in jeder Datenbank definieren und die Benutzer den jeweiligen Datenbanken hinzufügen. Die Rolle "SqlJDBCXAUser" ist ausschließlich in der Masterdatenbank definiert, da damit Zugriff auf die erweiterten gespeicherten SQL JDBC-Prozeduren gewährt wird, die sich in der Masterdatenbank befinden. Sie müssen den einzelnen Benutzern zuerst Zugriff auf die Masterdatenbank und dann auf die Rolle "SqlJDBCXAUser" gewähren. Dazu müssen Sie bei der Masterdatenbank angemeldet sein.

Beispiel

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

Siehe auch

Andere Ressourcen

Ausführen von Transaktionen mit dem JDBC-Treiber