【问题标题】:In JDBC, when autocommit is false and no explicit savepoints have been set, is it good style or a waste to rollback?在 JDBC 中,当 autocommit 为 false 且未设置显式保存点时,回滚是好风格还是浪费?
【发布时间】:2011-03-10 19:11:03
【问题描述】:

假设你有以下代码:

Connection conn;
try
{
   conn = ... // get connection
   conn.setAutoCommit(false);

   ... // Do some modification queries and logic

   conn.commit()
} catch(SQLException e)
{
    conn.rollback() // Do we need this?
    conn.close()
}

在这段代码中,如果出现异常,是关闭连接(因为自动提交已关闭)还是显式回滚然后关闭连接更好?没有存档点。

我觉得添加回滚调用可能有意义,因为:

1) 将来有人可能会添加保存点但忘记添加回滚

2) 提高可读性

3) 它不应该花费任何东西,对吧?

但显然,这些都不是特别引人注目。有什么标准做法吗?

注意:我知道在关闭和回滚时需要重复尝试/捕获。我实际上有一个中间件可以抽象数据库访问并处理它,但我想知道添加它是否是多余的。

【问题讨论】:

  • 好问题。我对您的第三项的答案很感兴趣,因为我们过去总是在返回连接之前进行回滚,但有一次我们的 DBA 注意到了这一点,并且非常虔诚地声称回滚是昂贵的。我没有得到很好的动力,但我们让步了,基本上确保只有在需要时才调用 rollback()(这很容易,因为它只是代码中受影响的一个地方)。
  • 有人能用 Java 7 try-with-resource 回答这个问题吗?这会简化必须编写的代码吗? (相关问题,但未说明如何结合使用 try-with-resource 和 autoCommit:stackoverflow.com/questions/8066501/…

标签: java jdbc


【解决方案1】:

正常的成语如下:

public void executeSomeQuery() throws SQLException {
    try (Connection connection = dataSource.getConnection()) {
        connection.setAutoCommit(false);

        try (PreparedStatement statement = connection.prepareStatement(SOME_SQL)) {
            // Fire transactional queries here.

            connection.commit();
        } catch (SQLException e) {
            connection.rollback();
            throw e;
        }
    }
}

请注意,当 try 块完成时,Java 7 的 try-with-resources statement 总是在资源上隐式调用 close(),就好像它发生在 finally 中一样。

调用rollback() 在涉及池连接时也是强制性的。也就是说,它将重置连接的事务状态。池连接的close() 不会这样做,只有commit()rollback() 会这样做。不调用rollback() 可能会导致池连接的下一个租约仍将在其内存中保留上一个事务的(成功)查询。

另请参阅Connection#close() 的 javadoc(重点不是我的):

强烈建议应用程序在调用 close 方法之前显式提交或回滚活动事务。如果调用close 方法并且存在活动事务,则结果是实现定义的。

【讨论】:

  • 啊。谢谢你。我不知道池连接(我们使用的)情况下的行为。我们的大部分遗留代码都没有使用它,而我的大部分代码都使用它。这是一个令人信服的理由。这在任何地方都有记录吗?
  • rollback() javadoc 提到:释放此Connection 对象当前持有的所有数据库锁。您可以将其理解为“事务锁”。将此与来自连接池的连接实际上 不会关闭,而只是返回池以在其他查​​询中重用的事实相结合。不提交/回滚它将冒险使用相同的锁 cq。交易。
  • @BalusC : 谢谢你:) 我经历过这个。如果我关闭从连接池获得的连接会怎样?
  • FWIW, commons-dbcp2BasicDataSource.setRollbackOnReturn(boolean),默认为true,用于自动回滚Connection.close()上的事务。
  • 设置autoCommit(false)的时候需要重新设置回true,还是只是暂时影响连接?
【解决方案2】:

关闭应该回滚,因为它不会在资源释放时提交,但最好让你的错误处理具体,所以如果你想回滚异常,就这样做。然后您可以在 finally{} 块中进行清理。 rollback() 仅在错误时发生,在这种情况下,您的 commit() 不成功,甚至没有达到。

Connection conn = null;
try {
    conn = ...

    ...
    conn.commit();
}
catch (SQLException e) {
    if (conn != null) {
        conn.rollback();
    }
}
finally {
    if (conn != null) {
        conn.close();
    }
}

【讨论】:

  • 请注意 close()rollback() 也可以(不幸地)抛出一个您必须处理的 SQLException(请参阅 BalusC 的答案)。
  • 是的,编译器会确保你处理这些,可能在调用代码中,但这可能相当难看。
  • 使用 jdk >=7 ,您可以使用 try-with-resources 语法对可关闭对象使用自动关闭行为。
猜你喜欢
  • 1970-01-01
  • 2014-11-20
  • 2011-08-31
  • 1970-01-01
  • 2010-11-04
  • 2014-10-31
  • 1970-01-01
  • 2010-10-20
  • 2012-04-01
相关资源
最近更新 更多