2015-09-21 94 views
5

爲什麼數據庫連接通常在兩個位置關閉,一次是在使用後直接關閉,另一個是在finally塊中使用檢查null以防止它們關閉兩次。使用finally塊不夠嗎? finally塊將在每種情況下執行。僅在finally塊中關閉JDBC連接?

這是Apache-Tomcat JNDI Datasource HOW-TO的官方示例。他們指出,在任何情況下都必須關閉連接。我想知道爲什麼僅僅使用finally-block是不夠的,因爲主try {}塊結尾的close命令似乎是重複的。

Connection conn = null; 
    Statement stmt = null; // Or PreparedStatement if needed 
    ResultSet rs = null; 
    try 
    { 
     conn = ... get connection from connection pool ... 
     stmt = conn.createStatement("select ..."); 
     rs = stmt.executeQuery(); 
     ... iterate through the result set ... 
     rs.close(); 
     rs = null; 
     stmt.close(); 
     stmt = null; 
     conn.close(); // Return to connection pool 
     conn = null; // Make sure we don't close it twice 
    } 
    catch (SQLException e) 
    { 
     ... deal with errors ... 
    } 
    finally 
    { 
     // Always make sure result sets and statements are closed, 
     // and the connection is returned to the pool 
     if (rs != null) 
     { 
      try 
      { 
       rs.close(); 
      } 
      catch (SQLException ignore) 
      { 
      } 
      rs = null; 
     } 
     if (stmt != null) 
     { 
      try 
      { 
       stmt.close(); 
      } 
      catch (SQLException ignore) 
      { 
      } 
      stmt = null; 
     } 
     if (conn != null) 
     { 
      try 
      { 
       conn.close(); 
      } 
      catch (SQLException ignore) 
      { 
      } 
      conn = null; 
     } 
    } 

我想寫出更短:

Connection conn = null; 
    Statement stmt = null; // Or PreparedStatement if needed 
    ResultSet rs = null; 
    try 
    { 
     conn = ... get connection from connection pool ... 
     stmt = conn.createStatement ("select ..."); 
     rs = stmt.executeQuery(); 
     ... iterate through the result set ... 
    } 
    catch (SQLException e) 
    { 
     // ... deal with errors ... 
    } 
    finally 
    { 
     // Always make sure result sets and statements are closed, 
     // and the connection is returned to the pool 
     try 
     { 
      if (rs != null) 
       rs.close(); 
      if (stmt != null) 
       stmt.close(); 
      if (conn != null) 
       conn.close(); 
     } 
     catch (SQLException ignore) 
     { 
     } 
    } 
+1

這是我第一次看到這個。我遇到的所有示例都只在finally塊中進行。我不認爲這是一種非常流行的做法。我認爲它增加了代碼的開銷和複雜性。 –

+2

甚至更​​短的'嘗試與資源';)但是,這個例子似乎過於複雜。 – Marvin

+2

如果rs.close()在您的代碼中引發異常,該怎麼辦?然後'stmt'和'conn'不會被關閉。 –

回答

6

你有一個很好的問題 - 我不明白「官方例如」要麼。最後塊肯定是足夠的。然而,你的代碼有更嚴重的錯誤,即如果rs.close()會拋出異常,你會有stmtconn泄漏出去,你也會無視地忽略這個異常。這是你不應該做的事情。從Java 7開始,使用try-with-resources結構是首選方式,但是如果你不能去那裏,至少分別處理每個可能的異常(rs,stmt,conn),這樣它們不會導致對方泄漏。

例如Apache commons DbUtils有closeQuietly()只是爲了這個目的,因爲它曾經是一種常見的情況。就我個人而言,我會去Spring JDBCTemplate之類的地方,在幕後做這種事。

編輯:試用資源由Oracle 解釋。總之,你會做這樣的事情:

try (Connection conn = yourCodeToGetConnection(); 
    Statement stmt = con.createStatement(); 
    ResultSet rs = stmt.executeQuery(query)) { 
    while (rs.next()) { 
     String coffeeName = rs.getString("COF_NAME"); 
     int supplierID = rs.getInt("SUP_ID"); 
     float price = rs.getFloat("PRICE"); 
     int sales = rs.getInt("SALES"); 
     int total = rs.getInt("TOTAL"); 

     System.out.println(coffeeName + ", " + supplierID + ", " + 
           price + ", " + sales + ", " + total); 
    } 
} catch (SQLException ex) { 
    // log, report or raise 
} 

凡試語句自動connstmtrs在所有情況下,爲了關閉(在你的國家,它們以相反的順序)的交易。可能的例外,你仍然需要處理自己。

+0

你能解釋一下try-with-resources結構嗎?至今對此並不在意。 –

+0

@ tombo_189添加了快速說明 – eis

+0

我從來不知道嘗試使用資源。爲+1。官方網頁的代碼似乎也讓我感到困惑。 –

0

感謝您的評論。因此,總結(尤其是對我的問題[關閉Connection將關閉基礎語句並關閉語句的EJP註釋會自動關閉ResultSet]),因爲我認爲try-with-resource構造有點難以閱讀建議書寫

Connection conn = null; 
Statement stmt = null; // Or PreparedStatement if needed 
ResultSet rs = null; 
try 
{ 
    conn = ... get connection from connection pool ... 
    stmt = conn.createStatement ("select ..."); 
    rs = stmt.executeQuery(); 
    ... iterate through the result set ... 
} 
catch (SQLException e) 
{ 
    // ... deal with errors ... 
} 
finally 
{ 
    // Always make sure result sets and statements are closed, 
    // and the connection is returned to the pool 
    try 
    { 
     if (conn != null) 
      conn.close(); 
    } 
    catch (SQLException ignore) 
    { 
    } 
} 

只關閉主連接,同時使Statement和ResultSet始終保持不變並由Connection關閉。

對不對?

+0

那麼,這可能是一個辯論的主題,你可以在[這個線程]得到(http://stackoverflow.com/questions/4507440/must-jdbc-resultsets-and-statements-be-closed-separately-although -連接)。通常連接將關閉其他,但AFAIK它不能保證。國際海事組織(IMO)僅僅讓資源嘗試,JDBCTemplate或類似的方法來確保事物被關閉是比較容易的。但我並沒有聲稱這是錯的。另外,我個人至少會記錄是否有任何SQLExceptions,即使你不提高它們。 – eis

0

我寧願你寫一個關閉連接的常用方法。可以從finally塊中調用。

像這樣的事情

Connection conn = null;   
Statement stmt = null; // Or PreparedStatement if needed   
ResultSet rs = null; 

try 
{ 
    conn = ... get connection from connection pool ...    
    stmt = conn.createStatement ("select ...");   
    rs = stmt.executeQuery(); 

    ... iterate through the result set ... 

} 
catch (SQLException e) 
{ 
    // ... deal with errors ... 
} 
finally 
{ 
    CloseTheConnection(conn);   
    CloseTheStatement(stmt);   
} 

public void closeTheConnection(Connection conn){    
try{ 
    if(conn!=null){    
    conn.close();    
    }    
}catch(Exception ex){    
} 

pubic void closeTheStatement(Statement stmt){    
try{    
if(stmt != null)    
     stmt.close();    
    } catch(Exception ex){     
    } 
} 

通過創建不同的方法和從最後稱他們將確保即使你從一個方法得到任何異常的另一種方法肯定會被調用。它也可以重用。

+0

介意你的代碼中的空白...我修復了代碼塊,但是你應該自己修復空白以保持可讀性和一致性 – eis

+0

當然,這是我在這裏的第一個答案,甚至難以發佈:D。會照顧那:) –