Grundlegendes zu XA-Transaktionen

Microsoft SQL Server JDBC Driver unterstützt optional verteilte Transaktionen für die Java-Plattform, Enterprise Edition 5/JDBC 2.0. JDBC-Verbindungen von der SQLServerXADataSource-Klasse können Bestandteil normaler verteilter Transaktionsverarbeitungsumgebungen wie Anwendungsservern für die Java-Plattform, Enterprise Edition 5 (Java EE 5) 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".

Richtlinien und Einschränkungen für die Verwendung von XA-Transaktionen

Die folgende Liste enthält Informationen über bestimmte Einschränkungen und Richtlinien für die Verwendung von XA-Transaktionen.

  • Unter Windows XP:

    Wenn Sie XA-Transaktionen mit SQL Server über den Microsoft SQL Server JDBC-Treiber verwenden, funktionieren XA-Transaktionen möglicherweise nicht ordnungsgemäß. 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 Windows XP und XA-Transaktionen.

  • Unter Windows Server 2003:

    Wenn Sie XA-Transaktionen zusammen mit MS DTC (Microsoft Distributed Transaction Coordinator) unter Windows Server 2003 verwenden, stellen Sie möglicherweise fest, 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.

Die folgenden zusätzlichen Richtlinien gelten für eng verkoppelte Transaktionen:

  • Wenn Sie XA-Transaktionen zusammen mit MS DTC (Microsoft Distributed Transaction Coordinator) verwenden, stellen Sie möglicherweise fest, 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 Ressourcen-Manager anzeigen, beispielsweise SQL Server.

  • Ab Version 1.2 stellt der Microsoft SQL Server 2005 JDBC-Treiber 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:

Unter Windows XP und Windows Server 2003:

  1. Klicken Sie in der Systemsteuerung auf Verwaltung, und öffnen Sie dann Komponentendienste. Sie können auch auf Start und auf Ausführen klicken, im Dialogfeld Öffnen den Befehl dcomcnfg eingeben und dann zum Öffnen der Komponentendienste auf OK klicken.

  2. Erweitern Sie Komponentendienste und Computer, 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 Sie SQL Server, und starten Sie diesen erneut, damit die MS DTC-Änderungen synchronisiert werden.

Unter Windows Vista:

  1. Klicken Sie auf Start, geben Sie im Feld Suche starten den Namen dcomcnfg ein, und drücken Sie dann die EINGABETASTE, um Komponentendienste zu öffnen. Sie können auch im Feld Suche starten den Pfad %windir%\system32\comexp.msc eingeben, um Komponentendienste zu öffnen.

  2. Erweitern Sie Komponentendienste, Computer, Arbeitsplatz und dann Distributed Transaction Coordinator.

  3. Klicken Sie mit der rechten Maustaste auf Lokaler DTC, und wählen Sie dann Eigenschaften aus.

  4. Klicken Sie im Dialogfeld Eigenschaften von Lokaler DTC auf die Registerkarte Sicherheit.

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

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

  7. Beenden Sie SQL Server, und starten Sie diesen 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 Itanium-Prozessor verwenden, verwenden Sie die Datei sqljdbc_xa.dll im Ordner IA64.

  2. Führen Sie auf allen SQL Server-Instanzen, die an verteilten Transaktionen teilnehmen, das Datenbankskript xa_install.sql aus. Mit diesem Skript werden die erweiterten gespeicherten Prozeduren installiert, die von sqljdbc_xa.dll aufgerufen werden. Diese erweiterten gespeicherten Prozeduren implementieren die Unterstützung für verteilte Transaktionen und XA für Microsoft SQL Server JDBC Driver 2.0. 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 der Rolle "SqlJDBCXAUser" hinzu.

Sie können auf jeder SQL Server-Instanz nur eine Version der Assembly sqljdbc_xa.dll gleichzeitig konfigurieren. Anwendungen müssen möglicherweise unterschiedliche Versionen des JDBC-Treibers verwenden, um mithilfe der XA-Verbindung eine Verbindung mit der gleichen SQL Server-Instanz herzustellen. In diesem Fall muss sqljdbc_xa.dll, die Teil des neuesten JDBC-Treibers ist, auf der SQL Server-Instanz installiert werden.

Sie können die derzeit auf der SQL Server-Instanz installierte Version von sqljdbc_xa.dll auf drei Arten überprüfen:

  1. Öffnen Sie das Verzeichnis LOG des SQL Server-Computers, der an verteilten Transaktionen teilnehmen soll. Wählen Sie die SQL Server-Datei ERRORLOG aus, und öffnen Sie diese. Suchen Sie in der Datei ERRORLOG nach dem Text "Version ... von 'SQLJDBC_XA.dll' wird verwendet".

  2. Öffnen Sie das Verzeichnis Binn des SQL Server-Computers, der an den verteilten Transaktionen teilnehmen soll. Wählen Sie die Assembly sqljdbc_xa.dll aus.

    1. Unter Windows Vista: Klicken Sie mit der rechten Maustaste auf sqljdbc_xa.dll, und wählen Sie dann Eigenschaften aus. Klicken Sie dann auf die Registerkarte Details. Im Feld Dateiversion wird die Version von sqljdbc_xa.dll angezeigt, die derzeit auf der SQL Server-Instanz installiert ist.

    2. Unter Windows XP und Windows Server 2003: Klicken Sie mit der rechten Maustaste auf sqljdbc_xa.dll, und wählen Sie dann Eigenschaften aus. Klicken Sie dann auf die Registerkarte Version. Im Feld Dateiversion wird die Version von sqljdbc_xa.dll angezeigt, die derzeit auf der SQL Server-Instanz installiert ist.

  3. Legen Sie die Protokollfunktion wie im Codebeispiel im nächsten Abschnitt fest. Suchen Sie in der Ausgabeprotokolldatei nach dem Text "Server-XA-DLL-Version ...".

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

Siehe auch

Andere Ressourcen

Ausführen von Transaktionen mit dem JDBC-Treiber