批量更新:connection.commit() 在最后,使用 setAutoCommit(false),但数据不会回滚
Batch Update: connection.commit() at the very end, with setAutoCommit(false), but data doesn't get rolled back
我的 Java JDBC 批量更新请求结构为
conn.setAutoCommit(false);
for (int i = 0; i < items.size(); i++) {
//...
ps.addBatch();
}
ps.executeBatch();
// Suppose an exception happens right before a Commit
if (someCondition)
throw new Exception("test");
conn.commit(); // Commit at the very end
我的理解是,当发生该异常时,我从未达到 commit
。所以我的数据不应该被保留,对吧?只要commit
在最后,并且指定setAutoCommit(false);
,任何异常都不需要回滚?
但我发现数据 确实 得到了持久化。我的问题是为什么?我需要 connection.rollback();
吗? (这是一个 Postgres 数据库)
请看一下PostgreSQL的事务隔离级别:
https://www.postgresql.org/docs/9.5/transaction-iso.html
对你有好处的default is READ COMMITTED。
回读 'uncommited' 更改取决于您使用的 JDBC 连接。根据事务隔离级别,每个连接都会有不同的 'view' 数据。
设置时的最佳实践
conn.setAutoCommit(false);
是在成功时提交并在 catch 子句中回滚:
conn.setAutoCommit(false);
try {
...
ps.executeBatch();
conn.commit();
} catch (Exception e) {
conn.rollback();
}
既不调用提交也不调用回滚将使连接保持打开状态,从而增加数据库的负载以维护未提交的更改。然后 JVM 将关闭连接,数据库将回滚所有数据。
JEE 容器在发生未捕获的异常时自动回滚事务。
每个事务都需要提交或回滚才能结束。如果您禁用了自动提交并且不调用 connection.commit()
,则它不会提交。
any exceptions don't require a rollback
So my data shouldn't be persisted, right?
数据以某种方式持久化,是的。活动事务保持其数据隔离,因此同一连接上 运行 的语句将看到更改,但其他连接在调用提交之前不会看到更改。
Postgres 使用多版本并发控制,这意味着插入、更新和删除实质上会创建行的“版本”,并且 Postgres 在内存中跟踪哪些事务可以看到哪些行的哪些版本。
那么,如果您的事务既不提交也不回滚会怎样?
在事务提交或回滚之前,锁将锁定在事务中更改的行上,这将阻止将来对相同数据的更改。这很容易导致中断。
始终提交或回滚您的事务。永远不要让它们悬空。
如果您从不提交或回滚事务会怎样?
我不太确定 Postgres 或您的应用程序中发生了什么。许多网络都有空闲套接字超时,因此最终您的连接将终止并触发回滚。 Postgres 也可以有自己的空闲事务超时(不确定 - 有根据的猜测)。但永远不要依赖这些东西。你无法控制伤害。
始终提交或回滚您的事务。永远不要让它们悬空。
这对您的代码意味着什么?
当您的异常被抛出时,调用堆栈将展开,跳过提交。所以你的交易不会提交。根据捕获异常的位置,conn
对象可能在也可能不在范围内。在范围内捕获它,以便您可以回滚。为什么?因为你应该:
始终提交或回滚您的事务。永远不要让它们悬空。
我的 Java JDBC 批量更新请求结构为
conn.setAutoCommit(false);
for (int i = 0; i < items.size(); i++) {
//...
ps.addBatch();
}
ps.executeBatch();
// Suppose an exception happens right before a Commit
if (someCondition)
throw new Exception("test");
conn.commit(); // Commit at the very end
我的理解是,当发生该异常时,我从未达到 commit
。所以我的数据不应该被保留,对吧?只要commit
在最后,并且指定setAutoCommit(false);
,任何异常都不需要回滚?
但我发现数据 确实 得到了持久化。我的问题是为什么?我需要 connection.rollback();
吗? (这是一个 Postgres 数据库)
请看一下PostgreSQL的事务隔离级别:
https://www.postgresql.org/docs/9.5/transaction-iso.html
对你有好处的default is READ COMMITTED。
回读 'uncommited' 更改取决于您使用的 JDBC 连接。根据事务隔离级别,每个连接都会有不同的 'view' 数据。
设置时的最佳实践
conn.setAutoCommit(false);
是在成功时提交并在 catch 子句中回滚:
conn.setAutoCommit(false);
try {
...
ps.executeBatch();
conn.commit();
} catch (Exception e) {
conn.rollback();
}
既不调用提交也不调用回滚将使连接保持打开状态,从而增加数据库的负载以维护未提交的更改。然后 JVM 将关闭连接,数据库将回滚所有数据。
JEE 容器在发生未捕获的异常时自动回滚事务。
每个事务都需要提交或回滚才能结束。如果您禁用了自动提交并且不调用 connection.commit()
,则它不会提交。
any exceptions don't require a rollback
So my data shouldn't be persisted, right?
数据以某种方式持久化,是的。活动事务保持其数据隔离,因此同一连接上 运行 的语句将看到更改,但其他连接在调用提交之前不会看到更改。
Postgres 使用多版本并发控制,这意味着插入、更新和删除实质上会创建行的“版本”,并且 Postgres 在内存中跟踪哪些事务可以看到哪些行的哪些版本。
那么,如果您的事务既不提交也不回滚会怎样?
在事务提交或回滚之前,锁将锁定在事务中更改的行上,这将阻止将来对相同数据的更改。这很容易导致中断。
始终提交或回滚您的事务。永远不要让它们悬空。
如果您从不提交或回滚事务会怎样?
我不太确定 Postgres 或您的应用程序中发生了什么。许多网络都有空闲套接字超时,因此最终您的连接将终止并触发回滚。 Postgres 也可以有自己的空闲事务超时(不确定 - 有根据的猜测)。但永远不要依赖这些东西。你无法控制伤害。
始终提交或回滚您的事务。永远不要让它们悬空。
这对您的代码意味着什么?
当您的异常被抛出时,调用堆栈将展开,跳过提交。所以你的交易不会提交。根据捕获异常的位置,conn
对象可能在也可能不在范围内。在范围内捕获它,以便您可以回滚。为什么?因为你应该:
始终提交或回滚您的事务。永远不要让它们悬空。