仅在finally块中关闭JDBC-Connections?

时间:2015-09-21 10:30:56

标签: java tomcat jdbc

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

以下是Apache-Tomcat JNDI Datasource HOW-TO的官方示例。他们指出,在任何情况下都必须关闭连接。我想知道为什么使用finally-block是不够的,因为主try {} -block结束时的close-commands似乎是redundent。

    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)
        {
        }
    }

3 个答案:

答案 0 :(得分:6)

你有一个很好的问题 - 我不理解"官方例子"无论是。最后一块就足够了。

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

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

编辑:Oracle here解释了try-with-resources。简而言之,您可以这样做:

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-statement会自动处理connstmtrs在所有情况下按顺序关闭(与您声明的顺序相反)。您仍需要处理的可能例外情况。

答案 1 :(得分:0)

感谢您的评论。所以总结(特别是EJP对我的问题的评论[关闭一个Connection将关闭底层语句并关闭一个Statement它会自动关闭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关闭。

右?

答案 2 :(得分:0)

我更喜欢编写一个关闭连接的常用方法,可以从finally块调用。

这样的事情:

using System;
using System.Collections.Generic;
using System.Linq;


public class FakeDb{

    public class FakeColumn{
        public string ColumnName;
    }

    public class FakeTable{     
        public string TableName {get;set;}
        public IEnumerable<FakeColumn> Columns {get;set;}
    }

    public IEnumerable<FakeTable> Tables {get;set;}

    public FakeDb(){
        Tables = new List<FakeTable>(){
            new FakeTable(){
                TableName = "People",
                Columns = new List<FakeColumn>(){
                    new FakeColumn(){
                        ColumnName = "FirstName"    
                    },
                    new FakeColumn(){
                        ColumnName = "LastName" 
                    }
                }
            }
        };
    }

}

public class Table{
    public Guid TableGuid = Guid.NewGuid();

    public IEnumerable<Column> Columns;
}

public class Column{
    private Table _myTable;
    public Guid ColumneGuid = Guid.NewGuid();
    public Column(Table hostTable){
        _myTable = hostTable;
    }   
}

public class Program
{
    public static void Main()
    {
        var fakeDb = new FakeDb();

        var tableList = fakeDb.Tables.GroupBy(p => p.TableName, p => p, (tableName, values) => new {
            TableName = tableName,
            OtherValues = values
        }).Select(p => new Table(){
            Columns = p.OtherValues.Select(i => new Column(/* Pass parent here? */)).ToList()
        }).ToList();


    }
}

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