MCS*_*MCS 35 java jdbc resource-management
考虑一下代码:
PreparedStatement ps = null;
ResultSet rs = null;
try {
ps = conn.createStatement(myQueryString);
rs = ps.executeQuery();
// process the results...
} catch (java.sql.SQLException e) {
log.error("an error!", e);
throw new MyAppException("I'm sorry. Your query did not work.");
} finally {
ps.close();
rs.close();
}
Run Code Online (Sandbox Code Playgroud)
上面没有编译,因为都PreparedStatement.close()和ResultSet.close()抛出java.sql.SQLException.那么我要在finally子句中添加一个try/catch块吗?或者将close语句移动到try子句中?或者只是不打扰打电话?
eri*_*son 45
在Java 7中,不应显式关闭它们,而应使用自动资源管理来确保关闭资源并正确处理异常.异常处理的工作方式如下:
Exception in try | Exception in close | Result
-----------------+--------------------+----------------------------------------
No | No | Continue normally
No | Yes | Throw the close() exception
Yes | No | Throw the exception from try block
Yes | Yes | Add close() exception to main exception
| | as "suppressed", throw main exception
希望这是有道理的.允许漂亮的代码,像这样:
private void doEverythingInOneSillyMethod(String key)
throws MyAppException
{
try (Connection db = ds.getConnection()) {
db.setReadOnly(true);
...
try (PreparedStatement ps = db.prepareStatement(...)) {
ps.setString(1, key);
...
try (ResultSet rs = ps.executeQuery()) {
...
}
}
} catch (SQLException ex) {
throw new MyAppException("Query failed.", ex);
}
}
Run Code Online (Sandbox Code Playgroud)
在Java 7之前,最好使用嵌套的finally块,而不是测试null的引用.
我将展示的示例可能看起来很难看深嵌套,但实际上,设计良好的代码可能不会在同一个方法中创建连接,语句和结果; 通常,每个嵌套级别都涉及将资源传递给另一个方法,该方法将其用作另一个资源的工厂.使用这种方法,来自a close()的异常将掩盖try块内的异常.这可以克服,但它会导致代码更加混乱,并且需要一个自定义异常类,它提供Java 7中存在的"抑制"异常链.
Connection db = ds.getConnection();
try {
PreparedStatement ps = ...;
try {
ResultSet rs = ...
try {
...
}
finally {
rs.close();
}
}
finally {
ps.close();
}
}
finally {
db.close();
}
Run Code Online (Sandbox Code Playgroud)
Ste*_* B. 25
如果你真的在手动推出自己的jdbc,它肯定会变得混乱.最后的close()需要用自己的try catch包装,这至少是丑陋的.您无法跳过关闭,但是当连接关闭时,资源将被清除(如果您正在使用池,则可能不会立即清除).实际上,使用框架(例如hibernate)来管理数据库访问的一个主要卖点是管理连接和结果集处理,这样你就不会忘记关闭.
你可以做一些像这样简单的事情,至少可以隐藏这些混乱,并保证你不会忘记一些事情.
public static void close(ResultSet rs, Statement ps, Connection conn)
{
if (rs!=null)
{
try
{
rs.close();
}
catch(SQLException e)
{
logger.error("The result set cannot be closed.", e);
}
}
if (ps != null)
{
try
{
ps.close();
} catch (SQLException e)
{
logger.error("The statement cannot be closed.", e);
}
}
if (conn != null)
{
try
{
conn.close();
} catch (SQLException e)
{
logger.error("The data source connection cannot be closed.", e);
}
}
}
Run Code Online (Sandbox Code Playgroud)
然后,
finally {
close(rs, ps, null);
}
Run Code Online (Sandbox Code Playgroud)
小智 7
另请注意:
"当一个Statement对象关闭时,它的当前ResultSet对象(如果存在)也会关闭."
http://java.sun.com/j2se/1.5.0/docs/api/java/sql/Statement.html#close()
仅在最终关闭PreparedStatement并且仅在它尚未关闭时才足够.如果你想要非常特别,请关闭ResultSet FIRST,而不是在关闭PreparedStatement之后(关闭它之后,像这里的一些示例一样,实际上应该保证异常,因为它已经关闭).
基于 @erickson 的答案,为什么不像try这样在一个块中完成它呢?
private void doEverythingInOneSillyMethod(String key) throws MyAppException
{
try (Connection db = ds.getConnection();
PreparedStatement ps = db.prepareStatement(...)) {
db.setReadOnly(true);
ps.setString(1, key);
ResultSet rs = ps.executeQuery()
...
} catch (SQLException ex) {
throw new MyAppException("Query failed.", ex);
}
}
Run Code Online (Sandbox Code Playgroud)
请注意,您不需要ResultSet在try块内创建对象,因为对象关闭ResultSet时会自动关闭。PreparedStatement
当生成 ResultSet 对象的 Statement 对象被关闭、重新执行或用于从多个结果序列中检索下一个结果时,ResultSet 对象会自动关闭。
参考:https://docs.oracle.com/javase/7/docs/api/java/sql/ResultSet.html
我通常有一个实用方法可以关闭这样的事情,包括注意不要尝试使用空引用做任何事情.
通常如果close()抛出异常我实际上并不关心,所以我只记录异常并吞下它 - 但另一种选择是将其转换为RuntimeException.无论哪种方式,我建议在一个易于调用的实用方法中进行,因为您可能需要在很多地方执行此操作.
请注意,如果关闭PreparedStatement失败,您当前的解决方案将不会关闭ResultSet - 最好使用嵌套的finally块.
| 归档时间: |
|
| 查看次数: |
85884 次 |
| 最近记录: |