Search code examples
javasql-serverjdbcibatis

Why do I receive "Cannot invoke a rollback operation when the AutoCommit mode is set to "true".", when I did not execute a rollback?


Executing the following results in the exception "Cannot invoke a rollback operation when the AutoCommit mode is set to "true".

java.sql.Connection connection = DriverManager.getConnection(connectionString);
com.ibatis.common.jdbc.ScriptRunner scriptRunner = new ScriptRunner(connection, true, true);
java.io.StringReader reader = new StringReader("PRINT 1;");
scriptRunner.runScript(reader);
// Cannot invoke a rollback operation when the AutoCommit mode is set to "true".

There is no rollback referenced in my code, and I can find no option to disable it from being forced on me. I have looked through standalone.xml and tried setting jta=false. I found and set commitRequired=false, it was true, on sqlMapConfig\transactionManager, but that did not make a difference.

If the answer to this exists out there, I was not able to find it. All I find is someone saying "of course you cant rollback", and the result being to disable autocommit. I am not asking for a rollback, so where is it coming from? Per https://www.programcreek.com/java-api-examples/index.php?api=com.ibatis.common.jdbc.ScriptRunner example 3, what I am doing should work.

I want to execute a script in autocommit mode (the default MS SQL Server mode) without any transactions being involved or forced on me, except the autocommit transaction that surrounds each individual statement in the sql script. I do not want IMPLICIT_TRANSACTIONS ON, no BEGIN TRANSACTION, no COMMIT, and definitely no ROLLBACK.

Thank you for your time

Stack Trace:

com.microsoft.sqlserver.jdbc.SQLServerException: Cannot invoke a rollback operation when the AutoCommit mode is set to "true".
    at com.microsoft.sqlserver.jdbc.SQLServerException.makeFromDriverError(SQLServerException.java:191)
    at com.microsoft.sqlserver.jdbc.SQLServerConnection.rollback(SQLServerConnection.java:2037)
    at com.ibatis.common.jdbc.ScriptRunner.runScript(ScriptRunner.java:222)
    at com.ibatis.common.jdbc.ScriptRunner.runScript(ScriptRunner.java:114)
    at com.metric.DebugTests.DebugAutoCommit(DebugTests.java:32)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
    at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:678)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)

Solution

  • Based on a quick glance at ScriptRunner (not the exact version in the question, but close enough), we can see the following code

    if (!autoCommit) {
        conn.commit();
    }
    

    Now that's good, we only commit if autocommit isn't on. However, if an exception is thrown, the finally clause is executed and autocommit isn't considered:

    } finally {
      conn.rollback();
      flush();
    }
    

    This will result in the losing of the original exception details.

    Since the exception does print logs if a logger is set, I recommend doing something like:

    scriptRunner.setErrorLogWriter(somePrintWriter); // Some PrintWriter, maybe to a file
    

    That way you'll find out the original exception. This is also definitely a bug, so it needs to be reported (seems I don't have github credentials, so someone that has those could put up an issue (or hey, why not submit a pull request)).