【问题标题】:Closing JDBC-Connections only in finally-block?仅在 finally-block 中关闭 JDBC-Connections?
【发布时间】:2015-12-18 00:30:21
【问题描述】:

为什么数据库连接经常在两个位置关闭,一次是在使用后直接关闭,其次是在 finally 块中使用 null 检查以防止它们被关闭两次。使用 finally 块还不够吗? finally 块将在每种情况下执行。

这是Apache-Tomcat JNDI Datasource HOW-TO 的官方示例。他们指出,在任何情况下都必须关闭连接。我想知道为什么在主 try {} 块的末尾使用 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 ...
        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)
        {
        }
    }

【问题讨论】:

  • 这是我第一次看到这个。我遇到的所有示例都只在 finally 块中进行。我不认为这是一种非常流行的做法。我认为它增加了代码的开销和复杂性。
  • 或者更短的try-with-resources ;) 但是,是的,这个例子似乎过于复杂了。
  • 如果rs.close() 在您的代码中抛出异常怎么办?那么stmtconn 将不会被关闭。
  • 对!应该将 Apache 示例中的所有三个关闭命令放在三个独立的 try-catch 块中。
  • Tomcat 示例代码质量很差,但您的代码没有改进。 Java 7 的正确解决方案是使用 try-with-resources。

标签: java tomcat jdbc


【解决方案1】:

你有一个很好的问题 - 我也不懂“官方示例”。 finally 块肯定够用了。

但是,您的代码有更严重的错误,即如果 rs.close() 会抛出异常,您将有 stmtconn 泄漏,您也会默默地忽略该异常。那是你不应该做的事情。从 Java 7 开始,使用 try-with-resources 构造是首选方式,但如果你不能去那里,至少要分别处理每个可能的异常(rs、stmt、conn),这样它们就不会导致彼此泄漏。

例如 Apache commons DbUtils 有 closeQuietly() 只是为了这个目的,因为它曾经是一个常见的场景。就我个人而言,我会去像 Spring JDBCTemplate 这样在幕后做这类事情的地方。

编辑:try-with-resources 由 Oracle here 解释。简而言之,你会这样做:

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
}

try 语句自动处理connstmtrs 在所有情况下按顺序关闭(与您声明它们的相反顺序)。可能的例外情况您仍然需要自己处理。

【讨论】:

  • 您能解释一下 try-with-resources 构造吗?到目前为止还没有在意。
  • @tombo_189 添加了快速解释
  • 我从来不知道尝试使用资源。为此+1。官方页面的代码似乎也让我感到困惑。
  • @tombo_189 你可能想看看 Spring 的 jdbc 模板。
  • @eis:你为什么不把 Connection 本身放在第三个 try-with-resources 块中? Connection 不是要关闭的主要内容吗?
【解决方案2】:

感谢许多 cmets。所以总结一下(特别是 EJP cmets 对我的问题 [关闭连接将关闭基础语句并关闭语句将自行关闭 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)
    {
    }
}

只关闭主连接,同时保持语句和结果集始终保持不变并由连接关闭。

对吗?

【讨论】:

  • 嗯,这可能是一个争论的话题,您可以在this thread 中找到它。通常 Connection 会关闭其他的,但 AFAIK 不能保证。 IMO 让 try-with-resources、JDBCTemplate 或类似的东西更容易确保事情已经关闭。但我也没有声称这是错误的。另外,我个人至少会记录是否有任何 SQLExceptions,即使您不提出它们。
【解决方案3】:

我更喜欢编写一个关闭连接的通用方法,可以从 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){            
}

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

通过创建不同的方法并从 finally 调用它们将确保即使您从一个方法获得任何异常,也肯定会调用另一个方法。它也可以重复使用。

【讨论】:

  • 注意代码中的空白...我修复了代码块,但您应该自己修复空白以使其可读和一致
  • 你当然,这是我在这里的第一个答案,甚至很难发布:D。会处理的:)
猜你喜欢
  • 2021-03-15
  • 1970-01-01
  • 2014-10-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-06-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多