Informazioni sulle transazioni XA
Il driver JDBC di Microsoft SQL Server offre il supporto per le transazioni distribuite opzionali Java Platform, Enterprise Edition/JDBC 2.0. Le connessioni JDBC ottenute dalla classe SQLServerXADataSource possono partecipare ad ambienti standard di elaborazione delle transazioni distribuite quali i server applicazioni Java Platform, Enterprise Edition (Java EE).
In questo articolo, XA sta per l'architettura estesa.
Avviso
Microsoft JDBC Driver 4.2 (e versioni successive) per SQL include nuove opzioni di timeout per la funzionalità di rollback automatico delle transazioni non preparate esistente. Per ulteriori informazioni vedere Configurazione delle impostazioni di timeout sul lato server per il rollback automatico delle transazioni non preparate più avanti in questo argomento.
Osservazioni:
Le classi per l'implementazione delle transazioni distribuite sono le seguenti:
Classe | Implementazioni | Descrizione |
---|---|---|
com.microsoft.sqlserver.jdbc.SQLServerXADataSource | javax.sql.XADataSource | Class factory per le connessioni distribuite. |
com.microsoft.sqlserver.jdbc.SQLServerXAResource | javax.transaction.xa.XAResource | L'adattatore di risorse per il gestore delle transazioni. |
Nota
Le connessioni delle transazioni distribuite XA sono impostate sul livello di isolamento predefinito Read Committed.
Linee guida e limitazioni relative all'uso di transazioni XA
Le seguenti linee guida aggiuntive si applicano alle transazioni a "regime di controllo stretto ("tightly-coupled")":
Quando si usano le transazioni XA con Microsoft Distributed Transaction Coordinator (MS DTC), è possibile che la versione corrente di MS DTC non supporti il comportamento dei rami XA a regime di controllo stretto ("tightly-coupled"). In MS DTC è ad esempio presente un mapping uno-a-uno tra un ID di transazione dei rami XA (XID) e un ID di transazione MS DTC e il lavoro eseguito da ognuno dei rami XA a regime di controllo libero ("loosely-coupled") viene isolato da quello degli altri rami.
MS DTC fornisce anche il supporto per i rami XA strettamente associati in caso di mapping tra più rami XA con lo stesso ID di transazione globale (GTRID) e un singolo ID di transazione MS DTC. Questo supporto consente a più rami XA strettamente associati di vedere le modifiche degli altri rami nello strumento di gestione delle risorse, ad esempio SQL Server.
Un flag SSTRANSTIGHTLYCPLD consente alle applicazioni di usare le transazioni XA strettamente associate con ID di transazione dei rami XA (BQUAL) diversi ma con lo stesso ID di transazione globale (GTRID) e ID di formato (FormatID). Per utilizzare questa funzionalità è necessario impostare SSTRANSTIGHTLYCPLD nel parametro flag del metodo XAResource.start:
xaRes.start(xid, SQLServerXAResource.SSTRANSTIGHTLYCPLD);
Istruzioni di configurazione
Per utilizzare le origini dei dati XA con Microsoft Distributed Transaction Coordinator (MS DTC) per la gestione delle transazioni distribuite, è necessario utilizzare la procedura seguente. La procedura generale è la seguente:
- Verificare che il servizio MS DTC sia in esecuzione e venga avviato automaticamente.
- Configurare i componenti lato server.
- Configurare il timeout lato server (facoltativo).
- Concedere l'accesso agli utenti.
Esecuzione del servizio MS DTC
Il servizio MS DTC deve essere contrassegnato come Automatico in Service Manager per assicurarsi che sia in esecuzione all'avvio del servizio SQL Server. Per abilitare MS DTC per le transazioni XA, procedere come segue:
In Windows Vista e versioni successive:
Fare clic sul pulsante Start, digitare dcomcnfg nella casella Cerca del menu Start e quindi premere INVIO per aprire Servizi componenti. È inoltre possibile digitare
%windir%\system32\comexp.msc
nella casella StartSearch per aprire Servizi componenti.Espandere Servizi componenti, Computer, Computer locale, quindi Distributed Transaction Coordinator.
Fare clic con il pulsante destro del mouse su DTC locale e quindi scegliere Proprietà.
Fare clic sulla scheda Sicurezza nella finestra di dialogo Proprietà DTC locale.
Selezionare la casella di controllo Abilita transazioni XA e quindi fare clic su OK. Questa azione comporta il riavvio di un servizio MS DTC.
Fare di nuovo clic su OK per chiudere la finestra di dialogo Proprietà e quindi chiudere Servizi componenti.
Arrestare e quindi riavviare SQL Server per assicurarsi che venga eseguita la sincronizzazione con le modifiche di MS DTC. Questo passaggio è facoltativo per SQL Server 2019 e SQL Server 2017 CU 16 e versioni successive.
Configurazione dei componenti delle transazioni distribuite JDBC
I passaggi per la configurazione dei componenti lato server variano a seconda della versione del server di destinazione. Per controllare la versione del server, eseguire la query SELECT @@VERSION
sul server e visualizzare l'output. Per l'aggiornamento cumulativo (CU) 16 di SQL Server 2017 e versioni successive, seguire le istruzioni di SQL Server 2017 CU16 e versioni successive. Per le versioni precedenti di SQL Server, seguire le istruzioni di SQL Server 2017 CU15 e versioni precedenti.
SQL Server 2017 CU16 e versioni successive
Per abilitare i componenti necessari per l'esecuzione di transazioni distribuite XA tramite il driver JDBC, eseguire la stored procedure seguente.
EXEC sp_sqljdbc_xa_install
Per disabilitare i componenti, eseguire la stored procedure seguente.
EXEC sp_sqljdbc_xa_uninstall
Passare alla sezione Configurazione delle impostazioni di timeout lato server per il rollback automatico delle transazioni non preparate.
SQL Server 2017 CU15 e versioni precedenti
Nota
Questo si applica solo a SQL Server 2017 CU15 e versioni precedenti. Le funzioni fornite da sqljdbc_xa.dll sono già incluse in SQL Server 2017 CU16 e versioni successive.
I componenti della transazione distribuita JDBC sono inclusi nella directory xa dell'installazione del driver JDBC. Tali componenti includono i file xa_install.sql e sqljdbc_xa.dll. Se sono presenti versioni diverse di JDBC Driver in client diversi, è consigliabile usare il file sqljdbc_xa.dll più recente nel server.
Per configurare i componenti delle transazioni distribuite del driver JDBC, procedere come segue:
Copiare il nuovo file sqljdbc_xa.dll dalla directory di installazione del driver JDBC alla directory Binn di ogni computer con SQL Server che parteciperà alle transazioni distribuite.
Nota
Se si usano transazioni XA con una versione di SQL Server a 32 bit (applicabile solo a SQL Server 2014 o versioni precedenti), usare il file sqljdbc_xa.dll nella cartella x86, anche se SQL Server è installato in un computer con processore x64. Se si usano transazioni XA con una versione di SQL Server a 64 bit in un processore x64, usare il file sqljdbc_xa.dll nella cartella x64.
Eseguire lo script del database xa_install.sql in ogni istanza di SQL Server che parteciperà alle transazioni distribuite. Lo script installa le stored procedure estese chiamate da sqljdbc_xa.dll. Tali stored procedure estese implementano il supporto per XA e per le transazioni distribuite per Microsoft JDBC Driver per SQL Server. È necessario eseguire lo script come amministratore dell'istanza di SQL Server.
Per concedere a un utente specifico l'autorizzazione necessaria per partecipare alle transazioni distribuite con il driver JDBC, aggiungere l'utente al ruolo SqlJDBCXAUser.
È possibile configurare solo una versione dell'assembly sqljdbc_xa.dll alla volta in ogni istanza di SQL Server. È possibile che le applicazioni debbano usare versioni diverse del driver JDBC per connettersi alla stessa istanza di SQL Server tramite la connessione XA. In tal caso è necessario installare nell'istanza di SQL Server il file sqljdbc_xa.dll, fornito con la versione più recente del driver JDBC.
È possibile verificare la versione del file sqljdbc_xa.dll attualmente installata nell'istanza di SQL Server in tre modi diversi:
Aprire la directory LOG del computer con SQL Server che parteciperà alle transazioni distribuite. Selezionare e aprire il file "ERRORLOG" di SQL Server. Eseguire la ricerca della frase "Using 'SQLJDBC_XA.dll' version ..." nel file "ERRORLOG".
Aprire la directory Binn del computer con SQL Server che parteciperà alle transazioni distribuite. Selezionare l'assembly sqljdbc_xa.dll.
- In Windows Vista o versione successiva: fare clic con il pulsante destro del mouse su sqljdbc_xa.dll, quindi scegliere Proprietà. Fare quindi clic sulla scheda Dettagli. Il campo Versione file mostra la versione di sqljdbc_xa.dll attualmente installata nell'istanza di SQL Server.
Impostare la funzionalità di registrazione come illustrato nell'esempio di codice della sezione successiva. Eseguire la ricerca della frase "Server XA DLL version:..." nel file del log di output.
Aggiornamento di sqljdbc_xa.dll
Nota
Questo si applica solo a SQL Server 2017 CU15 e versioni precedenti. Le funzioni fornite da sqljdbc_xa.dll sono già incluse in SQL Server 2017 CU16 e versioni successive.
Quando si installa una nuova versione del driver JDBC, è consigliabile utilizzare il file sqljdbc_xa.dll della nuova versione per aggiornare il file sqljdbc_xa.dll nel server.
Importante
Aggiornare il file sqljdbc_xa.dll durante il periodo di manutenzione o quando non è in corso nessuna transazione MS DTC.
Scaricare il file sqljdbc_xa.dll usando il comando Transact-SQL:
DBCC sqljdbc_xa (FREE)
Copiare il nuovo file sqljdbc_xa.dll dalla directory di installazione del driver JDBC alla directory Binn di ogni computer con SQL Server che parteciperà alle transazioni distribuite.
La nuova DLL verrà caricata quando viene chiamata una procedura estesa in sqljdbc_xa.dll. Non è necessario riavviare SQL Server per caricare le nuove definizioni.
Configurazione delle impostazioni di timeout lato server per il rollback automatico delle transazioni non preparate
Sono disponibili due impostazioni del Registro di sistema (valori DWORD) per controllare il comportamento di timeout delle transazioni distribuite:
XADefaultTimeout
(in secondi): il valore di timeout predefinito da utilizzare quando l'utente non specifica alcun timeout. Il valore predefinito è 0.XAMaxTimeout
(in secondi): il valore massimo di timeout che un utente può impostare. Il valore predefinito è 0.
Queste impostazioni sono specifiche dell'istanza di SQL Server e devono essere create nella seguente chiave del Registro di sistema:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\MSSQL<version>.<instance_name>\XATimeout
Nota
Per SQL Server a 32 bit in esecuzione su computer a 64 bit (applicabile solo a SQL Server 2014 e versioni precedenti), le impostazioni del registro devono essere create con la seguente chiave: HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Microsoft SQL Server\MSSQL<version>.<instance_name>\XATimeout
Un valore di timeout viene impostato per ogni transazione all'avvio e SQL Server esegue il rollback della transazione se il timeout scade. Il timeout viene determinato in base a queste impostazioni del Registro di sistema e a seconda di ciò che ha specificato l'utente tramite XAResource.setTransactionTimeout(). Di seguito sono riportati alcuni esempi su come vengono interpretati questi valori di timeout:
XADefaultTimeout = 0
,XAMaxTimeout = 0
Indica che non verrà utilizzato alcun timeout predefinito e non verrà applicato alcun timeout massimo per i client. In questo caso, le transazioni avranno un timeout solo se il client lo imposta utilizzando XAResource.setTransactionTimeout.
XADefaultTimeout = 60
,XAMaxTimeout = 0
Indica che tutte le transazioni avranno un timeout di 60 secondi se il client non specifica alcun timeout. Se il client specifica un timeout, verrà utilizzato tale valore di timeout. Non viene applicato alcun valore massimo per il timeout.
XADefaultTimeout = 30
,XAMaxTimeout = 60
Indica che tutte le transazioni avranno un timeout di 30 secondi se il client non specifica alcun timeout. Se il client specifica un timeout, viene utilizzato il timeout del client, a condizione che sia minore di 60 secondi (il valore massimo).
XADefaultTimeout = 0
,XAMaxTimeout = 30
Indica che tutte le transazioni avranno un timeout di 30 secondi (il valore massimo) se il client non specifica alcun timeout. Se il client specifica un timeout, viene utilizzato il timeout del client, a condizione che sia minore di 30 secondi (il valore massimo).
Configurazione dei ruoli definiti dall'utente
Per concedere a un utente specifico l'autorizzazione necessaria per partecipare alle transazioni distribuite con il driver JDBC, aggiungere l'utente al ruolo SqlJDBCXAUser. Usare ad esempio il codice Transact-SQL seguente per aggiungere un utente denominato "shelly" (utente di accesso standard SQL denominato "shelly") al ruolo SqlJDBCXAUser:
USE master
GO
EXEC sp_grantdbaccess 'shelly', 'shelly'
GO
EXEC sp_addrolemember [SqlJDBCXAUser], 'shelly'
I ruoli SQL definiti dall'utente vengono definiti per database. Per creare il proprio ruolo al fine di ottenere maggiore sicurezza, sarà necessario definire il ruolo in ogni database e aggiungere gli utenti in modo specifico per ogni database. Il ruolo SqlJDBCXAUser è rigorosamente definito nel database master poiché viene usato per concedere l'accesso alle stored procedure estese SQL JDBC che risiedono nel master. Sarà necessario concedere prima l'accesso al master ai singoli utenti, quindi consentire l'accesso al ruolo SqlJDBCXAUser una volta connessi al database master.
Esempio
import java.net.Inet4Address;
import java.sql.*;
import java.util.Random;
import javax.sql.XAConnection;
import javax.transaction.xa.*;
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 = "<password>";
String connectionUrl = prefix + serverName + ":" + portNumber + ";encrypt=true;databaseName=" + databaseName + ";user="
+ user + ";password=" + password;
Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
try (Connection con = DriverManager.getConnection(connectionUrl); Statement stmt = con.createStatement()) {
stmt.executeUpdate("CREATE TABLE XAMin (f1 int, f2 varchar(max))");
}
// 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();
try (Connection 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);
}
xaCon.close();
// Open a new connection and read back the record to verify that it worked.
try (Connection con = DriverManager.getConnection(connectionUrl); Statement stmt = con.createStatement();
ResultSet rs = stmt.executeQuery("SELECT * FROM XAMin")) {
rs.next();
System.out.println("Read -> xid = " + rs.getString(2));
stmt.executeUpdate("DROP TABLE XAMin");
}
}
}
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);
}
}