2016-11-24 72 views
4

我正在用Java 8編寫一個事務。首先,我的代碼是這樣的。在Java 8中回滾()語句執行失敗的最佳方式是什麼?

try (Connection conn = DAOUtil.getConnection(); 
    PreparedStatement ps = conn.prepareStatement(addSubscriptionSql)) { 
    conn.setAutoCommit(false); 
    //do work 
    conn.commit(); 
} catch (SQLException e) { 
    e.printStackTrace(); //handle error 
} 

但由於我應該在事務失敗的情況下回滾,所以我必須像這樣更改代碼。請注意兩個try塊。

try (Connection conn = DAOUtil.getConnection()) { 
    try (PreparedStatement ps = conn.prepareStatement(addSubscriptionSql)) { 
     conn.setAutoCommit(false); 
     //do work 
     conn.commit(); 
    } catch (SQLException e) { 
     conn.rollback(); 
     e.printStackTrace(); //handle error 
    } 
} catch (SQLException e) { 
    e.printStackTrace(); //handle error 
} 

我的問題是,有沒有更好的(我的意思是更簡單)的方式呢?我可以通過一個try塊來實現嗎?

回答

6

您可以使用

try(Connection conn = DAOUtil.getConnection(); 
    PreparedStatement ps = conn.prepareStatement(addSubscriptionSql); 
    AutoCloseable finish = conn::rollback) { 

    conn.setAutoCommit(false); 
    //do work 
    conn.commit(); 
} 

這總是會調用rollback(),但commit()成功完成後,回滾將成爲空操作,因爲它的commit()上次成功完成後重置狀態到...

由於AutoCloseable聲明要拋出Exception這將需要處理這種廣泛的異常類型。它可以固定,可能是在其它情況下也有用一個自定義類型:

interface SQLCloseable extends AutoCloseable { 
    @Override public void close() throws SQLException; 
} 

...

try(Connection conn = DAOUtil.getConnection(); 
    PreparedStatement ps = conn.prepareStatement(addSubscriptionSql); 
    SQLCloseable finish = conn::rollback) { 

    conn.setAutoCommit(false); 
    //do work 
    conn.commit(); 
} 

現在,只有異常類型SQLException的處理被執行。

如果你不喜歡的rollback()的想法被無條件調用,該解決方案變得不那麼優雅:

boolean[] success = { false }; 
try(Connection conn = DAOUtil.getConnection(); 
    PreparedStatement ps = conn.prepareStatement(addSubscriptionSql); 
    SQLCloseable finish =() -> { if(!success[0]) conn.rollback(); }) { 

    conn.setAutoCommit(false); 
    //do work 
    conn.commit(); 
    success[0] = true; 
} 

如果你在最後復位自動提交狀態,您可以使用,作爲一個指標爲了回滾的必要性:

try(Connection conn = DAOUtil.getConnection(); 
    PreparedStatement ps = conn.prepareStatement(addSubscriptionSql); 
    SQLCloseable finish =() -> { if(!conn.getAutoCommit()) conn.rollback(); }) { 

    conn.setAutoCommit(false); 
    //do work 
    conn.commit(); 
    conn.setAutoCommit(true); 
} 
+0

聰明!感謝分享。 –

+1

這是一個聰明的解決方案,所以+1,但我想知道有多少人會在代碼庫中遇到這個問題。 –

5

我可以用一個try塊來實現嗎?

不,因爲您的conn對象不會在您的試用資源的catch塊中可用。如果您希望在執行PreparedStatement時捕獲異常並明確執行conn.rollback(),則回滾將不得不在創建conn對象的資源嘗試的try內發生(即對於PreparedStatement使用第二個嵌套的try塊呼叫)。

相關問題