如何正确清理Java中的JDBC资源?

如何正确清理Java中的JDBC资源?,java,jdbc,Java,Jdbc,清理JDBC资源时,什么被认为是最佳实践?为什么?我保持了示例的简短,因此只是清理结果集 finally { if(rs != null) try{ rs.close(); } catch(SQLException ignored) {} } 对 finally { try{ rs.close(); } catch(Exception ignored) {} } 就我个人而言,我赞成第二种选择,因为它稍微短一点。对此,我们非常感谢您的任何意见 我看你的第二个(不常见的)版本没

清理JDBC资源时,什么被认为是最佳实践?为什么?我保持了示例的简短,因此只是清理结果集

finally
{
  if(rs != null)
    try{ rs.close(); } catch(SQLException ignored) {}
}

finally
{
  try{ rs.close(); } catch(Exception ignored) {}
}

就我个人而言,我赞成第二种选择,因为它稍微短一点。对此,我们非常感谢您的任何意见

我看你的第二个(不常见的)版本没有问题

ResultSet rs = //initialize here
try {
  // do stuff here
} finally {
  try { rs.close(); }
  catch(SQLException ignored) {}
}
  • 通常情况下,rs不会为空,因此在极少数情况下会发生NPE。因此,我认为这里没有性能问题
  • 如果
    rs=null
唯一的缺点是,如果我们有多个资源要关闭,那么如果我们想关闭尽可能多的资源,我们就必须为每个资源添加一个try/catch。否则,我们将在catch子句中输入第一个
null
,这可能会导致未屏蔽的泄漏

所以看起来是这样的:

finally {
   try{rs.close();  }catch(Exception ignored){}
   try{stmt.close();}catch(Exception ignored){}
   try{conn.close();}catch(Exception ignored){}
}

。。。这仍然是可读和可理解的。但是,根据不改变一个共同的模式,我将坚持旧式的测试方法:<代码> null <代码>,在关闭的同时捕获<代码> SqLExcExo/<代码>

< p>如果您正在编写一个长时间运行的应用程序,则应该考虑连接池。

ApacheDBCP项目为您做了大量这方面的工作。您还可以看看SpringJDBC或Hibernate之类的东西


Spring使用对象池,并添加了一些非常好的方法来抽象JDBC的缺点。

我倾向于使用以下方法。我认为检查
null
很好,因为它显示了您的意图,即您确实意识到这些对象在极少数情况下可能为null。(空检查也比创建
NullPointerException
要快)我还认为最好记录异常,而不是吞并它们。在
close
失败的情况下,我想知道它并将其保存在日志文件中

finally {            
            if (rs != null) {
                try {
                    rs.close();
                } catch (SQLException e) {
                 LOG.warn("Failed to close rs", e);
                }
            }
            if (st != null) {
                try {
                    st.close();
                } catch (SQLException e) { 
                 LOG.warn("Failed to close st", e);     
                }
            }
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException e) {
                 LOG.warn("Failed to close conn", e);
                }
            }
        }
如果要经常执行此操作,请使用静态方法创建一个实用程序类来关闭ResultSet、语句和连接,而不是一次又一次地复制和粘贴此代码

使用,您可以非常简洁地执行此清理,如下所示:

 finally {            
            DBUtils.closeQuietly(rs);
            DBUtils.closeQuietly(st);
            DBUtils.closeQuietly(conn);            
        }

正如其他人所指出的,JDBC资源(语句、结果集等)很少
null
。如果是的话,你手头的问题比
NullPointerException
s更大。在这方面,
NullPointerException
s将有助于提醒您JDBC驱动程序出现严重问题。如果您的JDBC驱动程序实际上为您提供了
null
引用,那么在调用
close()
之前典型的
null
检查将隐藏问题

同样,并非所有JDBC驱动程序都严格遵循该规范。例如,当关联的
语句
关闭时,某些驱动程序不会自动关闭
结果集
。因此,您必须确保显式关闭
结果集
及其
语句
(叹气)

在实践中,我发现这项技术很有用(尽管它不是最漂亮的):


这项技术保证执行每个
close()
语句,从
ResultSet
开始并向外执行<如果驱动程序向您提供
null
引用,则仍然会抛出code>NullPointerExceptions,但出于开头解释的原因,我允许这样做<如果<代码>关闭()/代码>语句失败(我认为这是一件好事——我想知道是否出了问题),仍然会抛出代码> SqLExcExe>/Calp>S。

< P>这是我的JDK 6的方法。如果您有JDK 7+的话,最好使用我在这里描述的方法

  • 它很短
  • 它定义了一个可以静态导入的close方法
  • 它避免了空的捕捉块
  • 它处理可能发生的任何SQLException(即使在getConnection或close方法中)
  • 它是零安全的

如今,JDK 7为您提供了清理资源的最简单选项:

String query = "select COF_NAME, PRICE from COFFEES";
try (Statement stmt = con.createStatement()) {
    ResultSet rs = stmt.executeQuery(query);
    while (rs.next()) {
        String coffeeName = rs.getString("COF_NAME");
        float price = rs.getFloat("PRICE");
        System.out.println(coffeeName + ", "  + price);
    }
}

try语句确保在语句末尾关闭每个资源。请参见

不要考虑第二个选项-它也可以用于关闭流。顺便说一句-我喜欢你的名字,因为
忽略了
例外,我会记住:-)不管怎样,忘了“保持示例简短”:-)我推荐Vickie在下面的回复。期望异常作为正常代码流的一部分抛出从来都不是一个好的实践。在第二个首选块中,您依赖异常来处理空ResultSet的情况,而不是自己检查。如果ResultSet初始化返回错误(如果数据库配置错误或无响应),会发生什么情况?@Jean应该发生什么?在这种情况下,它不需要关闭。+1表示没有使用
null
s执行愚蠢的操作-1用于您的异常处理。@Tom这不是我的异常处理:)帖子只是避免以相同的方式处理NPE和SQLException。您可以在关闭每个异常之前使用Logger。我想你知道这一点,但还有一次——最糟糕的一次<代码>空愚蠢。吞咽异常。第7项:避免终结器(有效的Java)我知道它与final不同,感谢你的for(;;){print('!')}+1没有对
null
s做一些愚蠢的事情-1对于您的异常处理,以及对于如何初始化三个局部变量显得有点困惑。@Tom-这只是对OPs建议(版本2)所暗示的后果的评估。我想这已经足够清楚了……即使可以忽略这些罪行,你最终还是合并了
块<代码>最后
块不会互相挤压。我就是这样做的。我不在“finally”catch块中做任何日志记录。我倾向于在catch块中添加日志记录
PreparedStatement statement = connection.prepareStatement("...");
try {
    ResultSet results = statement.executeQuery();
    try {
        while (results.next()) {
            // ...
        }
    } finally {
        results.close();
    }
} finally {
    statement.close();
}
private void querySomething() {
    Connection connection = null;
    PreparedStatement statement = null;
    ResultSet rs = null;
    try {
        // get connection
        // prepare statement
        // execute query
        // and so on
    } catch (SQLException e) {
        throw new MyException("Error while talking to database", e);
    } finally {
        close(connection, statement, rs);
    }
}

// useful because you probably have more than one method interacting with database
public static void close (Connection connection, Statement statement, ResultSet rs) {
    if (rs != null) {
        try { rs.close(); } catch (Exception e) { _logger.warning(e.toString()); }
    }
    if (statement != null) {
        try { statement.close(); } catch (Exception e) { _logger.warning(e.toString()); }
    }
    if (connection != null) {
        try { connection.close(); } catch (Exception e) { _logger.warning(e.toString()); }
    }
}
String query = "select COF_NAME, PRICE from COFFEES";
try (Statement stmt = con.createStatement()) {
    ResultSet rs = stmt.executeQuery(query);
    while (rs.next()) {
        String coffeeName = rs.getString("COF_NAME");
        float price = rs.getFloat("PRICE");
        System.out.println(coffeeName + ", "  + price);
    }
}
public static void close(Statement... statements) {
        for (Statement stmt : statements) {
            try {
                if (stmt != null)
                    stmt.close();
            } catch (SQLException se) {
            }// nothing we can do
        }
    }

    public static void close(Connection conn) {
        try {
            if (conn != null)
                conn.close();
        } catch (SQLException se) {
        }// nothing we can do
    }
public static void close(ResultSet rs) {
        try {
            if (rs != null) 
                rs.close();
        } catch (SQLException se) {
        }// nothing we can do
}