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 数据库总是在一个事务中,并且为你启动了一个新的。
一般来说,如果您想使用事务,您必须关闭自动提交。
在一个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 代码的设置。
分析基本正确。建议的修复不是。请参阅 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 数据库总是在一个事务中,并且为你启动了一个新的。
一般来说,如果您想使用事务,您必须关闭自动提交。