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

person dan.m was user2321368    schedule 06.11.2014    source источник


Ответы (1)


Я обнаружил причину такого поведения.

Я предположил, что jTDS после вызова setAutoCommit(false) явно начинает транзакцию для соединения. На самом деле так себя не ведет. Что он делает, так это выдает SET IMPLICIT_TRANSACTIONS ON для соединения.

По данным Microsoft (http://msdn.microsoft.com/en-us/library/ms187807.aspx) — «Когда IMPLICIT_TRANSACTIONS = ON, явная НАЧАЛО ТРАНЗАКЦИИ запустит две вложенные транзакции».

Например, если мы выполним следующее в 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 выполняет эти процедуры.

person dan.m was user2321368    schedule 10.11.2014