在哪里关闭java的PreparedStatements和ResultSets?

发布于 2024-07-09 14:42:12 字数 585 浏览 12 评论 0原文

考虑一下代码:

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 子句中? 或者只是懒得打电话关闭?

Consider the code:

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();
}

The above does not compile, because both PreparedStatement.close() and ResultSet.close() throw a java.sql.SQLException. So do I add a try/catch block to the finally clause? Or move the close statements into the try clause? Or just not bother calling close?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(13

国粹 2024-07-16 14:42:12

在 Java 7 中,您不应显式关闭它们,而应使用自动资源管理< /a> 确保资源 已关闭并对异常情况进行适当处理。 异常处理的工作原理如下:

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();
}

In Java 7, you should not close them explicitly, but use automatic resource management to ensure that resources are closed and exceptions are handled appropriately. Exception handling works like this:

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

Hopefully that makes sense. In allows pretty code, like this:

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

Prior to Java 7, it's best to use nested finally blocks, rather than testing references for null.

The example I'll show might look ugly with the deep nesting, but in practice, well-designed code probably isn't going to create a connection, statement, and results all in the same method; often, each level of nesting involves passing a resource to another method, which uses it as a factory for another resource. With this approach, exceptions from a close() will mask an exception from inside the try block. That can be overcome, but it results in even more messy code, and requires a custom exception class that provides the "suppressed" exception chaining present in Java 7.

Connection db = ds.getConnection();
try {
  PreparedStatement ps = ...;
  try {
    ResultSet rs = ...
    try {
      ...
    }
    finally {
      rs.close();
    }
  } 
  finally {
    ps.close();
  }
} 
finally {
  db.close();
}
平生欢 2024-07-16 14:42:12

如果您真的手动编写自己的 jdbc,它肯定会变得混乱。 finally 中的 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); 
}

If you're really hand-rolling your own jdbc it definitely gets messy. The close() in the finally needs to get wrapped with its own try catch, which, at the very least, is ugly. You can't skip the close, although the resources will get cleared when the connection is closed (which might not be right away, if you're using a pool). Actually, one of the main selling points of using a framework (e.g. hibernate) to manage your db access is to manage the connection and result set handling so you don't forget to close.

You can do something simple like this, which at least hides the mess, and guarantees that you don't forget something.

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

}

and then,

finally {
    close(rs, ps, null); 
}
耶耶耶 2024-07-16 14:42:12

对于文件 I/O,我通常在finally 块中添加一个try/catch。 但是,您必须小心,不要从finally块中抛出任何异常,因为它们将导致原始异常(如果有)丢失。

有关数据库连接关闭的更具体示例,请参阅本文

For file I/O, I generally add a try/catch to the finally block. However, you must be careful not to throw any exceptions from the finally block, since they will cause the original exception (if any) to be lost.

See this article for a more specific example of database connection closing.

游魂 2024-07-16 14:42:12

另请注意:

“当关闭 Statement 对象时,其当前 ResultSet 对象(如果存在)也将被关闭。”

http://java.sun.com/j2se/1.5.0/docs/api/java/sql/Statement.html#close()< /a>

仅在finally 中关闭PreparedStatement 就足够了,并且仅当它尚未关闭时才关闭。 如果您想真正特别,请首先关闭 ResultSet,而不是在关闭PreparedStatement之后(之后关闭它,就像这里的一些示例一样,实际上应该保证出现异常,因为它已经关闭了)。

Also note:

"When a Statement object is closed, its current ResultSet object, if one exists, is also closed. "

http://java.sun.com/j2se/1.5.0/docs/api/java/sql/Statement.html#close()

It should be sufficient to close only the PreparedStatement in a finally, and only if it is not already closed. If you want to be really particular though, close the ResultSet FIRST, not after closing the PreparedStatement (closing it after, like some of the examples here, should actually guarantee an exception, since it is already closed).

猫弦 2024-07-16 14:42:12

不要浪费时间编写低级异常管理代码,而是使用 Spring-JDBC 等更高级别的 API,或者连接/语句/rs 对象的自定义包装器,来隐藏混乱的 try-catch 代码。

Don't waste your time coding low-level exception management, use an higher-level API like Spring-JDBC, or a custom wrapper around connection/statement/rs objects, to hide the messy try-catch ridden code.

微凉徒眸意 2024-07-16 14:42:12

基于 @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);
  }
}

请注意,您不需要在 try 块内创建 ResultSet 对象,因为当 PreparedStatement 执行时 ResultSet 会自动关闭 对象已关闭。

当Statement对象出现时,ResultSet对象会自动关闭
生成它的被关闭、重新执行或用于检索下一个
由多个结果序列得出的结果。

参考: https://docs.oracle.com/javase /7/docs/api/java/sql/ResultSet.html

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

戈亓 2024-07-16 14:42:12

我通常有一个实用程序方法可以关闭这样的事情,包括注意不要尝试使用空引用执行任何操作。

通常,如果 close() 抛出异常,我实际上并不关心,所以我只是记录异常并吞下它 - 但另一种选择是将其转换为 RuntimeException 。 无论哪种方式,我建议使用易于调用的实用方法来执行此操作,因为您可能需要在很多地方执行此操作。

请注意,如果关闭PreparedStatement失败,您当前的解决方案将不会关闭ResultSet - 最好使用嵌套的finally块。

I usually have a utility method which can close things like this, including taking care not to try to do anything with a null reference.

Usually if close() throws an exception I don't actually care, so I just log the exception and swallow it - but another alternative would be to convert it into a RuntimeException. Either way, I recommend doing it in a utility method which is easy to call, as you may well need to do this in many places.

Note that your current solution won't close the ResultSet if closing the PreparedStatement fails - it's better to use nested finally blocks.

倾城°AllureLove 2024-07-16 14:42:12

如果您使用的是 Java 7,则可以使用那些实现 AutoCloseable (即 PreparedStatementResultset

您可能还会发现这个问题很有趣:在 Java 7 中关闭结果集

If your are using Java 7 you can use the improvements in the exception handling mechanisms in those classes that implement AutoCloseable (i.e. PreparedStatement, Resultset)

You might also find this question interesting: Closing ResultSet in Java 7

差↓一点笑了 2024-07-16 14:42:12

我知道这是一个老问题,但为了以防万一有人正在寻找答案,java 现在有了 try-with-resouce 解决方案。

static String readFirstLineFromFile(String path) throws IOException {
      try (BufferedReader br =
                   new BufferedReader(new FileReader(path))) {
        return br.readLine();
    }
}

I know this is an old question, but just in case someone is looking for the answer, java now has the try-with-resouce solution.

static String readFirstLineFromFile(String path) throws IOException {
      try (BufferedReader br =
                   new BufferedReader(new FileReader(path))) {
        return br.readLine();
    }
}
爱人如己 2024-07-16 14:42:12

不要省略调用 close。 这可能会导致问题。

我更喜欢在finally 中添加try/catch 块。

Do no omit calling close. It may cause problems.

I prefer adding try/catch block to the finally.

无所的.畏惧 2024-07-16 14:42:12

重点是finally子句,

finally {
   try {
      rs.close();
      ps.close();
   } catch (Exception e) {
      // Do something
   }
}

我觉得你要修改2点。

首先,使用 try & 在 fainlly 子句中再次捕获。

其次,在执行 ps.close() 之前执行 rs.close()。

[电子邮件受保护]

focus finally clause,

finally {
   try {
      rs.close();
      ps.close();
   } catch (Exception e) {
      // Do something
   }
}

I think you have to modify 2 points.

First, use try & catch again in fainlly clause.

Second, do rs.close() before doing ps.close().

[email protected]

可是我不能没有你 2024-07-16 14:42:12

可能是一种古老的(虽然简单)的做事方式,但它仍然有效:

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();
        }               
    }
}

Probably an old (though simple) way to do things, but it still works:

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();
        }               
    }
}
只为一人 2024-07-16 14:42:12

我用这个..

finally
{
    if (ps != null) ps.close();
    if (rs != null) rs.close();
}

I use this..

finally
{
    if (ps != null) ps.close();
    if (rs != null) rs.close();
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文