JDBC Informix 事务回滚

JDBC Informix Transaction Rollback

在一个maven项目中,我正在连接一个informix数据库,我想在发生异常时进行回滚。 informix 似乎不支持回滚,我收到以下异常。

java.sql.SQLException: Not in transaction.
    at com.informix.util.IfxErrMsg.buildExceptionWithMessage(IfxErrMsg.java:416)
    at com.informix.util.IfxErrMsg.buildIsamException(IfxErrMsg.java:401)
    at com.informix.jdbc.IfxSqli.addException(IfxSqli.java:3096)
    at com.informix.jdbc.IfxSqli.receiveError(IfxSqli.java:3368)
    at com.informix.jdbc.IfxSqli.dispatchMsg(IfxSqli.java:2292)
    at com.informix.jdbc.IfxSqli.receiveMessage(IfxSqli.java:2217)
    at com.informix.jdbc.IfxSqli.executeRollback(IfxSqli.java:646)
    at com.informix.jdbc.IfxSqliConnect.rollback(IfxSqliConnect.java:2124)
    at com.company.helpers.DBDriver.makeConnection(DBDriver.java:72)
    at com.company.Main.main(Main.java:40)

这里是有问题的片段。调用connection.rollback()时出现异常。出于测试目的,我在 try 子句上调用了 rollback

     try{
        connection = DriverManager.getConnection(this.url,this.username,this.password);
        LOGGER.info("Database connection successful.");
        statement = connection.createStatement();
        resultSet = statement.executeQuery("select FIRST 10 * from clients");
        while (resultSet.next()) {
            System.out.println(resultSet.getString("id") + ", " + resultSet.getString("name"));
        }

        String sql = "UPDATE clients\n" +
                "\tSET idhost=5\n" +
                "\tWHERE id=9058;\n";
        statement.executeUpdate(sql);
        connection.rollback(); //temporary to test rollback() 
        //connection.commit();

      }
      catch (Exception e) {
        //connection.rollback();
        LOGGER.error("Errors occurred in database.");
        LOGGER.error(e.getMessage(), e);

      }

Informix 有 4 种类型的数据库:

  • 未登录
  • 缓冲日志记录
  • 无缓冲日志记录
  • (无缓冲日志记录和)MODE ANSI

未记录的数据库不支持事务。可能是您已连接到未记录的数据库。在您执行 BEGIN WORK(或只是 BEGIN)之前,缓冲和非缓冲日志记录数据库不会启动事务。您可能会失败,因为您没有明确开始交易。 MODE ANSI 数据库自动启动事务并保持它们打开直到显式提交或回滚,或者当会话终止时,在这种情况下它们被回滚。您始终可以在 MODE ANSI 数据库中回滚(或提交)。

JDBC 标准可能需要 'MODE ANSI' 语义。它们并不难模仿——驱动程序只需在适当的时间点发送 BEGIN WORK。

…嗯…

在异常堆栈跟踪的顶部,它显示“不在事务中”。那是错误-255;还有错误-256“交易不可用”。如果你连接到一个未记录的数据库,你会得到 -256。因为你得到-255,你必须连接到一个记录的数据库,但你没有明确地开始一个事务,所以你不能回滚它。在没有 BEGIN 后跟 COMMIT 或 ROLLBACK 的情况下,每个语句都是一个独立的事务。

要修复,您需要开始交易。这可能很简单:
Statement st = connection.createStatement;
int count = st.executeUpdate("BEGIN WORK");
st.close();

或者可以使用 'execute immediate' 选项。代码未经测试;我不是 JDBC 程序员,也没有测试 JDBC 代码的设置。

分析基本正确。建议的修复不是。请参阅 by Brian Hughes 以了解 autocommit 的讨论,它提供了解决问题的正确方法。

这实际上是使用 JDBC 自动提交时的预期行为。 Informix JDBC 驱动程序默认启用自动提交。看 https://docs.oracle.com/javase/tutorial/jdbc/basics/transactions.html对此作简要介绍。

所以你执行的每个语句都会自动提交。 JDBC 本质上为每个查询 and/or 语句执行 BEGIN WORK、EXECUTE、COMMIT WORK。

因此,当您显式执行 connection.rollback() 时,它将失败,因为您的所有语句都已提交并且您在技术上不在事务中。

您可以使用 connection.setAutoCommit(false) 关闭此功能,然后您可以发出 SQL 语句 "BEGIN WORK" 来启动事务并 connection.rollback() 将其回滚。

正如 Jonathan 所指出的,另一个怪癖是,如果您使用 ANSI 兼容数据库而不是普通的日志数据库。如果它是 ANSI,那么你不需要“BEGIN WORK”,因为 ANSI 数据库总是在一个事务中,并且为你启动了一个新的。

一般来说,如果您想使用事务,您必须关闭自动提交。