【问题标题】:What is the suitable way to close the database connection in Java?用Java关闭数据库连接的合适方法是什么?
【发布时间】:2014-08-22 13:46:48
【问题描述】:

我试图关闭数据库连接。但是有点困惑,比如说

ResultSet rs = null 

我是否必须靠近它

rs.close();

或者

DatabaseUtil.closeResultSet(rs);

它们之间有什么区别?

【问题讨论】:

  • rs.close()rs = null 的情况下会抛出NullPointerException,并且还需要处理或声明抛出SQLException。大概DatabaseUtil.closeResultSet(ResultSet) 会为您处理这两个方面。

标签: java jdbc


【解决方案1】:

关闭结果集不会关闭数据库连接。您需要单独执行此操作。

通常您会希望像这样关闭资源:

if (resultSet != null) {
     try {
         resultSet.close();
     } catch (SQLException e) {
         log.info("closing resultset caused exception", e);
     }
}

如果这是您的 DatabaseUtil 所做的,那将是更可取的。

使用 try-with-resources 会产生更漂亮的代码,但请注意,一种极端情况的行为是非常不同的。如果在关闭结果集时抛出异常(在其他所有操作都没有问题之后),那么上面的代码不会传播它,因此它不会阻止其余事务的完成。但是,如果您使用 try-with-resources,则 close 方法抛出的异常将被传播。关闭结果集或语句时抛出的异常只是关于释放数据库资源(无论如何最终都会被释放,这不是危机),它并不表示任何数据问题,而且似乎不是破坏数据库的理由手术。

您应该在关闭结果集后关闭数据库连接。确保使用 finally 块,以便无论抛出什么异常(包括关闭语句或结果集时抛出的异常)都会关闭连接。

code in this questionnot 如何做到这一点的一个例子(很可能 OP 想要最小化示例中的样板,因为关闭事情不是问题的重点,所以它可能是为了保持简短,而不是因为OP不知道不这样做),它展示了现实生活中发生的常见错误:

Class Class1<T>
{
  public T getColumn(DataSource ds)
  {
    T value = null;
    Connection con = null;
    Statement st = null;

    try
    {
      con = ds.getConnection();
      st = con.createStatement();
      ResultSet rs = st.executeQuery("select 1 from dual");
      rs.next();
      Object o = rs.getObject(1); // I want an Integer but a BigDecimal is created!
      value = (T) o; // ClassCastException here!
    }
    finally
    {
      if (st != null) { st.close(); }
      if (con != null) { con.close(); }
    }

    return i;
  }
}

如果 statement.close 抛出 SQLException,那么 finally 块中的以下行将永远不会被调用,连接不会被关闭,并且数据库连接将一直处于未使用状态,直到超时。这可能看起来不错,因为它只是一个连接,但是每个其他连接可能会看到相同的行为,并且在适当的情况下,您可以通过这种方式关闭整个应用程序(丢弃所有数据库连接)。请参阅 Michael Nygard's book Release It! 了解一个故事,该故事描述了一个非常昂贵且痛苦的企业应用程序中断,该中断可追溯到完全像这样的代码。

不得不剪切和粘贴这个关闭资源的代码是丑陋的,引入类似模板方法的东西来传递结果集映射器将是一种改进,这样关闭只在一个地方完成。但这会让您走上重塑 spring-jdbc 的道路,它已经为您完成了所有这些工作。

【讨论】:

    【解决方案2】:

    那些方法只关闭ResultSet。您仍然必须关闭所有 StatementConnection 实例。我建议在 finally 块中这样做。类似的,

    Connection conn = null;
    Statement stmt = null'
    ResultSet rs = null;
    try {
      conn = getConnection();
      stmt = conn.prepareStatement(sql);
      stmt.setString(1, "Hello"); 
      rs = stmt.executeQuery();
      while (rs.next()) {
        // ...
      }
    } catch (SQLException se) {
      se.printStackTrace();
    } finally {
      if (rs != null) {
        try {
          rs.close();
        } catch (Exception e) {
          e.printStackTrace();
        }
      }
      if (stmt != null) {
        try {
          stmt.close();
        } catch (Exception e) {
          e.printStackTrace();
        }
      }
      if (conn != null) {
        try {
          conn.close();
        } catch (Exception e) {
          e.printStackTrace();
        }
      }
    }
    

    如果你使用我的Close utility,finally 块可能是,

    } finally {
      Close.close(rs, stmt, conn);
    }
    

    【讨论】:

      【解决方案3】:

      jdbc api 告诉我们关闭连接将关闭结果集和语句。关闭语句将关闭结果集。但是我总是自己关闭每个结果集和每个语句,因为我遇到了不这样做的问题。 只需使用结果集、语句和连接提供的 close 方法即可。

      【讨论】:

        【解决方案4】:

        我认为最好的方法是在 try-with-resources 中做所有事情

        try(conn=openConnection()){
          try(rs=conn.getResultSet()){
          }
        }
        

        这样您就可以完全确定资源最终会正确关闭。

        【讨论】:

          【解决方案5】:

          两种方式都可以,但我更喜欢第一个 rs.close(); ,不要忘记检查 rs 是否不为 null 并用 try..catch 语句括起您的调用,因此即使 Java7 为您关闭所有内容,您也必须在 finaly 块中关闭您的连接。

          查看Java / JDBC: Best design pattern to close database connection when exception occurs

          【讨论】:

            【解决方案6】:

            正常代码如下所示:

            Connection connection = null;
            PreparedStatement stmt = null;
            ResultSet rs = null;
            ResultSet rs2 = null;
            try {
              connection = getConnection();
              stmt = connection.prepareStatement("...");
            
              stmt.setString(":foobar", "Foobar");
              rs = stmt.execute ... ; // I don't remember the method return ResultSet
            
              while (rs.next()) {
                ...
              }
            
              stmt.setString(":foobar", "Barfoo");
              rs2 = stmt.execute ... ; 
            
            
            } finally {
              if (null != rs2) try {rs2.close();} catch (Exception e)
              if (null != rs) try {rs.close();} catch (Exception e) {...}
              if (null != stmt) try {stmt.close();} catch (Exception e) {...}
              if (null != connection) try {connection.close();} catch (Exception e) {...}
            }
            

            或者,在 Java 7 中,使用 try-with-resources(前面的代码几乎相同):

            try (Connection connection = getConnection();
                 PreparedStatement stmt = connection.prepareStatement("...")) {
            
              stmt.setString(":foobar", "Foobar");
              try (ResultSet rs = stmt.executeQuery()) {
                while (rs.next()) {
                    ...
                }
              }
              stmt.setString(":foobar", "Barfoo");
              try (ResultSet rs2 = stmt.executeQuery()) {
                while (rs2.next()) {
                    ...
                }
              }
            }
            

            区别很简单:

            • 您关闭ResultSet - 以释放相关资源。在这个例子中,我使用了PreparedStatement:你可以有参数,因此不同的ResultSet。因为它需要内存和资源,所以你需要在使用完它们后关闭它们,否则垃圾收集器会这样做,它只会回收内存。
            • 当您不再需要 PreparedStatementStatement 时将其关闭。关闭语句关闭关联的ResultSet
            • 当您不再需要 Connection 时,您可以关闭它。关闭连接会关闭Statements(和他们的ResultSet)。

            根据您的需要(例如:批处理与 Web 服务器),您可能不必在 Java 退出时关闭连接,但最好在不再需要资源时释放资源(无论是是 JDBC 资源或文件资源...)。

            【讨论】:

              【解决方案7】:

              关闭结果集不会关闭数据库连接。我们应该关闭连接,如下所示。 在关闭连接之前,您应该关闭其他实例,例如 ResultSet、PreparedStatement 和 Statement 实例。

              例如

               Connection con = DBUtils.getConnection();
               ....
               PreparedStatemet pstmt = con.getPreparedStatement();
               ResultSet rs = pstmt.execute();
              
               while(rs.next())
               {
               }
              

              // 一旦你完成了 ResultSet... 是时候关闭/释放实例了。

                  if (resultSet != null) {
                  try {
                  resultSet.close();
                  resultSet = null;
                  } catch (SQLException e) {
                    // ignore the exceptions.
                  }
                  }
              
                  // Close the Prepared Statement
                  if(theStatement != null)
                  {
                  try
                  {
                  theStatement.close();
                  theStatement = null;
                  }
                  catch(Exception ignored)
                  {
                  }
              
              // Close the Connection
                  if(theConnection != null)
                  {
                  try
                  {
                  theConnection.close();
                  theConnection = null;
                  }
                  catch(Exception ignored)
                  {
                  }
              

              【讨论】:

                猜你喜欢
                • 2019-04-14
                • 2011-05-05
                • 1970-01-01
                • 2015-10-09
                • 2012-12-29
                • 1970-01-01
                • 2018-11-19
                • 1970-01-01
                • 2015-04-29
                相关资源
                最近更新 更多