2012-03-28 38 views
0

我必須在一個函數中修改幾個表。他們必須全部成功,否則全部失敗。如果一個操作失敗,我希望他們都失敗。我有以下幾點:我們什麼時候需要調用java.sql.Connection.rollback()?

public void foo() throws Exception { 
    Connection conn = null; 
    try { 
     conn = ...; 
     conn.setAutoCommit(false); 
     grok(conn); 
     conn.commit(); 
    } 
    catch (Exception ex) { 
     // do I need to call conn.rollback() here? 
    } 
    finally { 
     if (conn != null) { 
      conn.close(); 
      conn = null; 
     } 
    } 
} 

private void grok(Connection conn) throws Exception { 
    PreparedStatement stmt = null; 
    try { 
     // modify table "apple" 
     stmt = conn.prepareStatement(...); 
     stmt.executeUpdate(); 
     stmt.close(); 

     // modify table "orange" 
     stmt = conn.prepareStatement(...); 
     stmt.executeUpdate(); 
     stmt.close(); 

     ... 
    } 
    finally { 
     if (stmt != null) { 
      stmt.close(); 
     } 
    } 
} 

我想知道如果我需要調用rollback()的情況下,在這個過程中出現問題的情況。

其他信息:我正在使用連接池。在上面的示例中,我還確保使用finally語句關閉每個PreparedStatement,爲簡潔起見,僅僅是省略了。

謝謝

回答

1

您無需致電rollback()。如果連接關閉但未完成commit()它將被回滾。

您無需設置connnull;並且由於在conn初始化之後try塊啓動(假設...無法計算爲空),因此finally中也不需要!= null

+0

確定更新以反映conn的例子可以是零,所以即使在更新我仍然不需要調用rollback()?謝謝 – user291701 2012-03-28 13:07:16

+0

正確,你不需要調用回滾。你之前的例子,'conn'在'try'之前被初始化好了 – 2012-03-28 14:13:05

+0

這是不正確的。您必須在連接上調用回滾以進行回滾更改。他們不會自動回滾給你。看到我的答案,但很容易通過在調用commit之前拋出異常來測試自己。 – 2017-05-21 19:24:43

0

如果您調用「commit」,那麼事務將被提交。如果您有多個插入/更新語句,並且其中一個失敗,則提交將導致無法提交到數據庫的插入/更新。所以是的,如果你不希望其他語句提交給數據庫,你需要調用回滾。通過將autocommit設置爲false,您實際上正在做的是允許多個語句一起提交或回滾。否則,每個單獨的語句將自動提交。

+0

爲了澄清我的答案,這假定你將有多個陳述。在你只有一條語句的例子中,它會自動回滾。但是如果你在Java代碼中調用了多個語句,那麼你會想要回滾。 – DavidB 2012-03-28 05:18:07

+0

我更新以顯示有多個語句(以及該變量被重用) - 我是否還需要回滾?謝謝 – user291701 2012-03-28 13:07:47

+0

您應該防守編碼。上面的代碼將使用單個連接,並且不需要顯式調用回滾。但是如果你想把這些代碼放到你有連接池的環境中,你必然會得到奇怪的行爲,因爲其他事務會重用同一個連接,因爲連接永遠不會在池中關閉。就我個人而言,我總是會使用回滾,除非您可以確定它是否會導致性能下降(回滾非常昂貴),並且您完全瞭解您所在環境的事務行爲。 – DavidB 2012-03-28 22:20:24

0

是的,如果您的任何語句失敗或您在調用commit之前檢測到異常,您需要調用回滾。這是一箇舊帖子,但接受的答案是錯誤的。您可以通過在提交之前拋出一個異常來自己嘗試一下,並且注意如果您不手動回滾,插入仍然會將其插入數據庫中。

JDBC文檔 https://docs.oracle.com/javase/tutorial/jdbc/basics/transactions.html#call_rollback

實施例校正從DOC用法

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); 
    } 
} 
相關問題