【问题标题】:When shall use setAutoCommit(true) in Java何时应在 Java 中使用 setAutoCommit(true)
【发布时间】:2018-12-26 18:24:30
【问题描述】:

我搜索了 getConnection().commit() (1) 与 getConnection().setAutoCommit(true) (2) 之间的区别。但是大多数情况只是描述(1)是默认方法,很少使用它来代替(2),或者两种方法都是“不同的目的”和相同的结果。 我使用 Oracle 文档中的示例 (https://docs.oracle.com/javase/tutorial/jdbc/basics/transactions.html#commit_transactions):

 public void updateCoffeeSales(HashMap<String, Integer> salesForWeek)
        throws SQLException {

        PreparedStatement updateSales = null;
        PreparedStatement updateTotal = null;

        String updateString =
            "update " + dbName + ".COFFEES " +
            "set SALES = ? where COF_NAME = ?";

        String updateStatement =
            "update " + dbName + ".COFFEES " +
            "set TOTAL = TOTAL + ? " +
            "where COF_NAME = ?";

        try {
            con.setAutoCommit(false);
            updateSales = con.prepareStatement(updateString);
            updateTotal = con.prepareStatement(updateStatement);

            for (Map.Entry<String, Integer> e : salesForWeek.entrySet()) {
                updateSales.setInt(1, e.getValue().intValue());
                updateSales.setString(2, e.getKey());
                updateSales.executeUpdate();
                updateTotal.setInt(1, e.getValue().intValue());
                updateTotal.setString(2, e.getKey());
                updateTotal.executeUpdate();
                con.commit();
            }
        } 
        catch (SQLException e ) {
            JDBCTutorialUtilities.printSQLException(e);
            if (con != null) {
                try {
                    System.err.print("Transaction is being rolled back");
                    con.rollback();
                } catch(SQLException excep) {
                    JDBCTutorialUtilities.printSQLException(excep);
                }
            }
        } 
        finally {
            if (updateSales != null) {
                updateSales.close();
            }
            if (updateTotal != null) {
                updateTotal.close();
            }
            con.setAutoCommit(true);
        }
    }
}

在这个例子中,我不知道finally 块中con.setAutoCommit(true) (1) 的用途,因为每个任务都已经从try 块提交,尽管它可能会发生异常。谁能给我一个清楚的解释什么时候应该使用(1)?非常感谢您的帮助。

如果默认方法是true,那么我只是简单地做一个commit()而不是(1)来结束一个事务块,下一个块方法(不需要管理这个块的事务)将是变为默认模式(1)并且我不需要再次将其设置为true?

我有一个假设,con.commit() 只是提交语句,它仍然会在 (1) 被再次调用之前锁定某些行/表。我想我误解了commit() 会自动将当前默认设置为true 而不是再次尝试调用(1),因为我已经测试了一些代码,在调用commit() 之后,来自行/表的所有锁都会全部释放,所以我的假设也是错误的。

【问题讨论】:

  • 如果您将其设置为true,则每条语句都是它自己的事务。这在某种程度上违背了符合 ACID 的 RDBMS 的目的。在这个例子中,程序员希望所有查询都成功或都不成功——这是 ACID 中的 A——原子性。简而言之,autoCommit 在实际应用程序中应该永远true - 它仅适用于玩具/点头用例。
  • (虽然这个例子看起来很糟糕,因为两个更新更新了同一张表的同一行)
  • 嗯,我在 Oracle 文档上搜索过,他们说“语句 con.setAutoCommit(true); 启用自动提交模式,这意味着每个语句在完成时会再次自动提交. 然后,您回到默认状态,您不必自己调用方法提交..." 这意味着如果我使用commit()rollback(),则翻译将完成并且我不需要@ 987654339@ (1) 不再是因为它是默认状态,对吧?我只是怀疑什么时候应该使用 (1) 或者它是旧的支持并且 commit() 是更新的可以替换 (1) 或者我误解了什么?

标签: java jdbc


【解决方案1】:

在您向我们展示的示例中,代码关闭了自动提交,然后在最后将其打开。当自动提交关闭时,它会一次重复执行两次更新,并在每对更新后提交一次。

在示例中,最后关闭自动提交的目的应该很清楚。程序员(大概!)想要执行一对更新并在它们之间进行提交因为这对更新需要以原子方式完成。如果您不关闭自动提交,则无法执行此操作。

finally 块中重新打开自动提交的目的是将其返回到默认状态。这大概是为了允许再次使用连接。然而,这是有必要的,这是值得商榷的:

  • 关闭或打开自动提交几乎不需要任何费用。
  • 如果连接已关闭自动提交,则调用setAutoCommit(false) 没有任何害处。

在我看来,上述的后果是有两种明智的方法来处理这个问题:

  • 如果整个应用程序需要显式事务,请在建立连接时关闭自动提交 .... 并将其关闭。

  • 如果应用程序的不同部分有不同的自动提交要求,那么最简单的方法是让管理每个事务的代码将自动提交设置为所需的值,然后不必费心重置它。

finally 块中恢复默认自动提交状态的 5 行左右的额外代码对我来说并不是必要的。实际上,除非您可以确保始终恢复自动提交状态,否则最好假设状态是不确定的;例如在updateCoffeeSales 方法的开头...或在其他一些希望启用自动提交的方法的开头。

【讨论】:

  • 是的,我认为finally 块是不必要的。因为我在 Oracle 文档上搜索过,他们说“如果事务被回滚或提交,事务将结束”所以默认状态将返回到 true 并且不再需要调用 setAutoCommit(true),对吗?我只是怀疑什么时候应该使用(1)或者它是旧的支持,commit() 是更新的可以替换(1)或者我误解了什么?
  • @mmo2112 与“旧”或“新”支持无关 - 与 SQL and transactional databases 的基本原理有关。
  • @Boris the Spider,我只是想知道 2 语句的确切用法,因为它们都意味着最终提交以结束 1 个事务。他们一定有不同的东西,我不确定。顺便说一句,我可以互相替换,代码仍然运行良好...
猜你喜欢
  • 2012-09-06
  • 2013-05-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-22
  • 2012-11-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多