【问题标题】:How to properly dispose of Connection, ResultSet, and Statement objects in a while loop如何在 while 循环中正确处理 Connection、ResultSet 和 Statement 对象
【发布时间】:2013-07-05 22:49:27
【问题描述】:

如果我有以下代码,这是否是关闭 Connection、ResultSet 和 Statement 对象的正确方法?我觉得所有对close() 的调用都应该在finally 块中。

Connection con = null;
ResultSet rs = null;
Statement stmt = null;

try{
    //Code before the while loop 
    con = DriveManager.getConnection("Stuff");

    while(someBoolean){          

        stmt = con.createStatement();
        rs = stmt.executeQuery("SQL query");

        // do stuff with query results.

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

        if( stmt != null){
               stmt.close();
        }

} //end while

    if( con != null ){
        con.close();
    }

catch (Exception e){
    //handle exception
}

【问题讨论】:

  • 我认为您不需要在循环中创建连接和语句。如果您使用的是 Java 7,请使用 try-with-resource,否则,将所有资源关闭的东西移动到 finally 块。
  • 我在连接上犯了一个错误,我实际上是在循环之外创建它。但你认为我也应该在外面创建声明吗?
  • "默认情况下,每个 Statement 对象只能同时打开一个 ResultSet 对象",除非您使用多个 ResultSet,否则我认为一个 Statement 就可以(这是我的理论,但我建议你进行一些测试)。

标签: java jdbc


【解决方案1】:

我建议使用嵌套的 try/catch 块,每个 资源 一个:

    final Connection conn = DriverManager.getConnection(jdbcUrl, jdbcUser,
            jdbcPassword);
    try {
        final Statement stmnt = conn.createStatement();
        try {
            final ResultSet rs = stmnt
                    .executeQuery("select * from pg_user");
            try {
                while (rs.next()) {
                    System.out.println(rs.getObject(1));
                }
            } finally {
                rs.close();
            }
        } finally {
            stmnt.close();
        }
    } finally {
        conn.close();
    }

每个 finally 子句中只有一个 single 行。否则,您必须将其包装在另一个 try/catch 中,以便在 finally 块中正确处理可能的异常。


一般来说,这种管理资源的模式(try/catch - 是否嵌套)会使代码变得混乱。摆脱的一种方法是集中管理您的资源,并使用侦听器/阅读器。

所以在库中你定义了一个Reader 和一个with... 方法:

interface ResultSetReader {
    void read(ResultSet rs) throws SQLException;
}

public static void withResultSet(final String query,
        final ResultSetReader reader) throws Exception {
    final Connection conn = getConnection();
    try {
        final Statement stmnt = conn.createStatement();
        try {
            final ResultSet rs = stmnt.executeQuery(query);
            try {
                while (rs.next()) {
                    reader.read(rs);
                }
            } finally {
                rs.close();
            }
        } finally {
            stmnt.close();
        }
    } finally {
        conn.close();
    }
}

像这样在调用站点与匿名类一起使用

    withResultSet("select * from pg_user", new ResultSetReader() {
        @Override
        public void read(ResultSet rs) throws SQLException {
            System.out.println(rs.getObject(1));
        }
    });

这样在使用现场没有try/catch,没有人忘记关闭资源(也没有人错误地关闭资源)。

如果 Java 8 可用,ResultSetReader 接口可以替换为闭包。

【讨论】:

    【解决方案2】:

    您无需在循环中创建Statement:您可以重复使用它。此外,使用 Java 7 try-with-resources (tutorial),您无需处理按正确顺序关闭等繁琐的工作。

    你可以这样做:

    try (
        Connection con = DriverManager.getConnection("Stuff");
        Statement stmt = con.createStatement();
    ){
        while(someBoolean){          
            try (ResultSet rs = stmt.executeQuery("SQL query")) {
                // do stuff with query results.
            }
        } //end while
    } catch (Exception e){
        //handle exception
    }
    

    如您所见,与自己管理相比,它需要的检查和语句要少得多,同时它确保以正确的顺序关闭资源,即使发生异常(即使在关闭其他资源之一时发生该异常)。例如,如果发生任何异常,您的旧代码无法关闭资源。

    【讨论】:

      【解决方案3】:

      是的,关闭资源应该在finally 块中,因为无论哪里可能引发异常,您都应该关闭所有资源。

      标准模式是:

      Connection con = null;
      ResultSet rs = null;
      Statement stmt = null;
      
      try {
          con = DriveManager.getConnection("Stuff");
          stmt = con.createStatement();
          rs = stmt.executeQuery("SQL query");
          // do stuff with query results
      } catch (SQLException e) { // Don't catch Exception, catch what you expect
          // handle exception
      } finally {
          // each close can itself explode, so wrap each in try catch
          try {
             if (rs != null)
                 rs.close();
          } catch (SQLException ignore) {} // no point handling
      
          try {
             if (stmt != null)
                 stmt.close();
          } catch (SQLException ignore) {} // no point handling
      
          try {
             if (con != null)
                 con.close();
          } catch (SQLException ignore) {} // no point handling
      }
      

      虽然关闭其中一个资源失败可能意味着其他资源也会爆炸,但尝试关闭每个资源仍然是一种好习惯,因此在 finally 块中单独尝试捕获。

      【讨论】:

      • 好吧,这看起来更像我认为我应该做的。 Statement 对象是否应该在 while 循环之外创建?
      • @rage 我已经完全从示例中删除了循环 - 它与问题的核心无关
      【解决方案4】:

      在 Java 7 中,您可以使用try-with-resource 语句:

      try(Connection con = getConnection(url, username, password, "drivername");
      Statement stmt = con.createStatement();
      ResultSet rs = stmt.executeQuery(sql);
      ) {
      
        //statements
      }catch(....){}
      

      在 Java 6 中,您可以关闭 finally 块中的资源:

      } finally {
         try { rs.close(); } catch (Exception e) {  }
         try { ps.close(); } catch (Exception e) {  }
         try { conn.close(); } catch (Exception e) {  }
      }
      

      你甚至可以使用辅助类来关闭连接。 Apache Commons DbUtils 有一个 DbUtils 类。

      } finally {
          DbUtil.closeQuietly(rs);
          DbUtil.closeQuietly(ps);
          DbUtil.closeQuietly(conn);
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2010-11-05
        • 2014-05-05
        • 2015-11-21
        • 2017-10-05
        • 1970-01-01
        • 1970-01-01
        • 2022-01-22
        相关资源
        最近更新 更多