考虑代码:
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();
}
以上内容无法编译,因为PreparedStatement.close()
和ResultSet.close()
都会抛出java.sql.SQLException
。那么我要在finally子句中添加一个try / catch块吗?或者将close语句移动到try子句中?或者只是不打扰打电话?
答案 0 :(得分:44)
在Java 7中,您不应该显式关闭它们,而是使用automatic resource management确保关闭resources并正确处理异常。异常处理的工作方式如下:
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);
}
}
在Java 7之前,最好使用嵌套的finally块,而不是测试null的引用。
我将展示的示例可能看起来很难看深嵌套,但实际上,精心设计的代码可能不会在同一个方法中创建连接,语句和结果;通常,每个嵌套级别都涉及将资源传递给另一个方法,该方法将其用作另一个资源的工厂。使用此方法,close()
的异常将掩盖try
块内的异常。这可以克服,但它会导致代码更加混乱,并且需要一个自定义异常类,它提供Java 7中存在的“抑制”异常链。
Connection db = ds.getConnection();
try {
PreparedStatement ps = ...;
try {
ResultSet rs = ...
try {
...
}
finally {
rs.close();
}
}
finally {
ps.close();
}
}
finally {
db.close();
}
答案 1 :(得分: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);
}
}
}
然后,
finally {
close(rs, ps, null);
}
答案 2 :(得分:9)
对于文件I / O,我通常会在finally块中添加try / catch。但是,您必须注意不要从finally块中抛出任何异常,因为它们会导致原始异常(如果有)丢失。
有关数据库连接关闭的更具体示例,请参阅this article。
答案 3 :(得分:7)
不要浪费时间编写低级异常管理,使用Spring-JDBC之类的高级API,或者连接/ statement / rs对象的自定义包装器来隐藏凌乱的try-catch驱动代码。 / p>
答案 4 :(得分:7)
另请注意:
“当一个Statement对象关闭时,它的当前ResultSet对象(如果存在)也会关闭。”
http://java.sun.com/j2se/1.5.0/docs/api/java/sql/Statement.html#close()
仅在最终关闭PreparedStatement并且仅在它尚未关闭时才足够。如果你想要非常特别,请关闭ResultSet FIRST,而不是在关闭PreparedStatement之后(关闭它之后,就像这里的一些示例一样,实际上应该保证异常,因为它已经关闭)。
答案 5 :(得分:5)
我通常有一个实用工具方法可以关闭这样的事情,包括注意不要尝试用空引用做任何事情。
通常如果close()
抛出异常,我实际上并不关心,所以我只记录异常并吞下它 - 但另一种方法是将其转换为RuntimeException
。无论哪种方式,我建议使用一种易于调用的实用方法,因为您可能需要在很多地方执行此操作。
请注意,如果关闭PreparedStatement失败,您当前的解决方案将不会关闭ResultSet - 最好使用嵌套的finally块。
答案 6 :(得分:2)
如果您使用的是Java 7,则可以在实现AutoCloseable的那些类中使用异常处理机制中的改进(即PreparedStatement
,Resultset
)
您可能还会发现这个问题很有趣:Closing ResultSet in Java 7
答案 7 :(得分:1)
我知道这是一个老问题,但是如果有人正在寻找答案,那么java现在有了try-with-resouce解决方案。
static String readFirstLineFromFile(String path) throws IOException {
try (BufferedReader br =
new BufferedReader(new FileReader(path))) {
return br.readLine();
}
}
答案 8 :(得分:0)
不要忽略呼叫关闭。它可能会导致问题。
我更喜欢将try / catch块添加到finally。
答案 9 :(得分:0)
可能是一种旧的(虽然简单)做事的方式,但它仍然有效:
public class DatabaseTest {
private Connection conn;
private Statement st;
private ResultSet rs;
private PreparedStatement ps;
public DatabaseTest() {
// if needed
}
public String getSomethingFromDatabase(...) {
String something = null;
// code here
try {
// code here
} catch(SQLException se) {
se.printStackTrace();
} finally { // will always execute even after a return statement
closeDatabaseResources();
}
return something;
}
private void closeDatabaseResources() {
try {
if(conn != null) {
System.out.println("conn closed");
conn.close();
}
if(st != null) {
System.out.println("st closed");
st.close();
}
if(rs != null) {
System.out.println("rs closed");
rs.close();
}
if(ps != null) {
System.out.println("ps closed");
ps.close();
}
} catch(SQLException se) {
se.printStackTrace();
}
}
}
答案 10 :(得分:0)
Building on @erickson's answer, why not just do it in one try
block like this?
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);
}
}
Note that you don't need to create the ResultSet
object inside the try
block as ResultSet
's are automatically closed when the PreparedStatement
object is closed.
A ResultSet object is automatically closed when the Statement object that generated it is closed, re-executed, or used to retrieve the next result from a sequence of multiple results.
Reference: https://docs.oracle.com/javase/7/docs/api/java/sql/ResultSet.html
答案 11 :(得分:-1)
关注最后一句,
finally {
try {
rs.close();
ps.close();
} catch (Exception e) {
// Do something
}
}
我认为你必须修改2分。
首先,使用try&在fainlly条款中再次捕获。
其次,在执行ps.close()之前先执行rs.close()。
fly1997@naver.com
答案 12 :(得分:-2)
我用这个..
finally
{
if (ps != null) ps.close();
if (rs != null) rs.close();
}