JTDS и транзакции
Я вижу различные варианты поведения, когда я вызываю хранимую процедуру (MSSQL 2008R2) непосредственно из SSMS или когда я вызываю ее из JTDS.
Во-первых, посмотрите эти две процедуры.
CREATE PROCEDURE [Template].[UnguardedTest]
@outparam_StartTransactionCount INT OUTPUT,
@outparam_TransactionCount INT OUTPUT
AS
BEGIN
SET NOCOUNT ON;
SET XACT_ABORT ON;
DECLARE @StartTranCount INT
SELECT @StartTranCount = @@TRANCOUNT
BEGIN TRANSACTION
BEGIN
SELECT @outparam_StartTransactionCount = @StartTranCount
SELECT @outparam_TransactionCount = @@TRANCOUNT
END
COMMIT TRANSACTION
END
Второе очень похоже на первое, за исключением того, что оно не начнет (и не совершит) транзакцию, кроме случаев, когда @@TRANCOUNT
на входе 0.
CREATE PROCEDURE [Template].[GuardedTest]
@outparam_StartTransactionCount INT OUTPUT,
@outparam_TransactionCount INT OUTPUT
AS
BEGIN
SET NOCOUNT ON;
SET XACT_ABORT ON;
DECLARE @StartTranCount INT
-- Record the @@TRANCOUNT at the beginning of this procedure / trigger.
SELECT @StartTranCount = @@TRANCOUNT
IF @StartTranCount = 0
BEGIN TRANSACTION
BEGIN
SELECT @outparam_StartTransactionCount = @StartTranCount
SELECT @outparam_TransactionCount = @@TRANCOUNT
END
IF @StartTranCount = 0
COMMIT TRANSACTION
END
Если я позвоню им из SSMS, с кодом ниже
DECLARE @outparam_TransactionCount INT
DECLARE @outparam_StartTransactionCount INT
EXECUTE [Template].[UnguardedTest] @outparam_StartTransactionCount OUTPUT, @outparam_TransactionCount OUTPUT
SELECT 'UNGUARDED_NOT_WRAPPED' AS Description, @outparam_StartTransactionCount AS [StartTranCount], @outparam_TransactionCount AS [TranCount]
BEGIN TRAN
EXECUTE [Template].[UnguardedTest] @outparam_StartTransactionCount OUTPUT, @outparam_TransactionCount OUTPUT
SELECT 'UNGUARDED_WRAPPED' AS Description, @outparam_StartTransactionCount AS [StartTranCount], @outparam_TransactionCount AS [TranCount]
COMMIT TRAN
EXECUTE [Template].[GuardedTest] @outparam_StartTransactionCount OUTPUT, @outparam_TransactionCount OUTPUT
SELECT 'GUARDED_NOT_WRAPPED' AS Description, @outparam_StartTransactionCount AS [StartTranCount], @outparam_TransactionCount AS [TranCount]
BEGIN TRAN
EXECUTE [Template].[GuardedTest] @outparam_StartTransactionCount OUTPUT, @outparam_TransactionCount OUTPUT
SELECT 'GUARDED_WRAPPED' AS Description, @outparam_StartTransactionCount AS [StartTranCount], @outparam_TransactionCount AS [TranCount]
COMMIT TRAN
Выход - то, что я ожидал.
Description StartTranCount TranCount
--------------------- -------------- -----------
UNGUARDED_NOT_WRAPPED 0 1
Description StartTranCount TranCount
----------------- -------------- -----------
UNGUARDED_WRAPPED 1 2
Description StartTranCount TranCount
------------------- -------------- -----------
GUARDED_NOT_WRAPPED 0 1
Description StartTranCount TranCount
--------------- -------------- -----------
GUARDED_WRAPPED 1 1
То есть обертывание вызова процедуры в транзакции приводит к тому, что StartTranCount равен 1, в противном случае он равен нулю.
Однако, когда я выполняю те же процедуры через JTDS/JDBC, как показано в коде ниже, я вижу странное поведение.
int tc = -1, startTC = -1;
final Connection con2 = DriverManager.getConnection(url);
con2.setAutoCommit(false);
final CallableStatement proc2 = con2.prepareCall("{ call Template.GuardedTest(?,?) }");
proc2.registerOutParameter("@outparam_StartTransactionCount", Types.INTEGER);
proc2.registerOutParameter("@outparam_TransactionCount", Types.INTEGER);
proc2.execute();
startTC = proc2.getInt("@outparam_StartTransactionCount");
tc = proc2.getInt("@outparam_TransactionCount");
log.info("Guarded StartTC: " + startTC + ", TC: " + tc);
proc2.close();
con2.commit();
con2.close();
final Connection con1 = DriverManager.getConnection(url);
con1.setAutoCommit(false);
final CallableStatement proc1 = con1.prepareCall("{ call Template.UnguardedTest(?,?) }");
proc1.registerOutParameter("@outparam_StartTransactionCount", Types.INTEGER);
proc1.registerOutParameter("@outparam_TransactionCount", Types.INTEGER);
proc1.execute();
startTC = proc1.getInt("@outparam_StartTransactionCount");
tc = proc1.getInt("@outparam_TransactionCount");
log.info("Unguarded StartTC: " + startTC + ", TC: " + tc);
proc1.close();
con1.commit();
con1.close();
Я вижу следующий вывод:
- Guarded StartTC: 0, TC: 2
- Unguarded StartTC: 0, TC: 2
Так как я ожидал увидеть те же значения, что и в "обернутом" примере выше (насколько я понимаю, JDBC начинает новую транзакцию при вызове setAutoCommit(false)
Я действительно в растерянности относительно того, что происходит. Любое понимание?
Дополнительная информация:
Если я переключаюсь на драйвер Microsoft JDBC, я получаю ожидаемые результаты
MSFT Driver - Guarded StartTC: 1, TC: 1
MSFT Driver - Unguarded StartTC: 1, TC: 2
1 ответ
Я обнаружил причину этого поведения.
Я предположил, что после JTDS setAutoCommit(false)
был вызван, явно начал транзакцию для соединения. На самом деле, это не так. Что он делает, это выдавать SET IMPLICIT_TRANSACTIONS ON
на связи.
Согласно Microsoft ( http://msdn.microsoft.com/en-us/library/ms187807.aspx) - "Когда IMPLICIT_TRANSACTIONS = ON, явный BEGIN TRANSACTION запустит две вложенные транзакции".
Например, если мы выполним следующее в SSMS
SET IMPLICIT_TRANSACTIONS ON
EXECUTE [Template].[UnguardedTest] @outparam_StartTransactionCount OUTPUT, @outparam_TransactionCount OUTPUT
SELECT 'UNGUARDED_IMPLICIT' AS Description, @outparam_StartTransactionCount AS [StartTranCount], @outparam_TransactionCount AS [TranCount]
COMMIT TRAN
SET IMPLICIT_TRANSACTIONS ON
EXECUTE [Template].[GuardedTest] @outparam_StartTransactionCount OUTPUT, @outparam_TransactionCount OUTPUT
SELECT 'GUARDED_IMPLICIT' AS Description, @outparam_StartTransactionCount AS [StartTranCount], @outparam_TransactionCount AS [TranCount]
COMMIT TRAN
Мы получаем следующее:
Description StartTranCount TranCount
------------------ -------------- -----------
UNGUARDED_IMPLICIT 0 2
Description StartTranCount TranCount
---------------- -------------- -----------
GUARDED_IMPLICIT 0 2
что согласуется с выводом, который мы получаем, когда jTDS выполняет эти процедуры.