为什么数据库连接经常在两个位置关闭,一次是在使用后直接关闭,其次是在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)
{
}
}
答案 0 :(得分:6)
你有一个很好的问题 - 我不理解"官方例子"无论是。最后一块就足够了。
然而,你的代码有更严重的错误,即如果rs.close()会抛出异常,你有stmt
和conn
泄漏,你也会默默地忽略该异常。这是你不应该做的事情。从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会自动处理conn
,stmt
和rs
在所有情况下按顺序关闭(与您声明的顺序相反)。您仍需要处理的可能例外情况。
答案 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调用它们将确保即使您从一个方法获得任何异常,也肯定会调用另一个方法。它也可以重复使用。