0

我的 JDBC 连接代码类似于 Java JDBC 教程中的以下代码:

public static void viewTable(Connection con) throws SQLException {
    Statement stmt = null;
    String query = "select COF_NAME, SUP_ID, PRICE, SALES, TOTAL from " + dbName + ".COFFEES";
    try {
      stmt = con.createStatement();
      ResultSet rs = stmt.executeQuery(query);
      while (rs.next()) {
        String coffeeName = rs.getString("COF_NAME");
        int supplierID = rs.getInt("SUP_ID");
        float price = rs.getFloat("PRICE");
        int sales = rs.getInt("SALES");
        int total = rs.getInt("TOTAL");
        System.out.println(coffeeName + "\t" + supplierID + "\t" + price + "\t" + sales + "\t" + total);
      }
    } catch (SQLException e ) {
      JDBCTutorialUtilities.printSQLException(e);
    } finally {
      stmt.close();
    }
  }

我对这种处理连接方式的问题是它关闭了finally块中的语句,并且该方法抛出了任何可能发生的 SQLException。我不想这样做,因为我希望在这个类中处理任何问题。但是,我确实希望Statement#close()在 finally 块中调用它,以便它始终关闭。

现在我将此代码放在一个单独的方法中,该方法返回一个返回HashMap的字段,该异常在类中处理。还有另一种可能更好的方法来处理这个问题吗?

编辑:close()SQLException 是我所关心的。如果可能的话,我想在方法中处理它。我可以在 finally 中写一个 try/catch,但这看起来真的很尴尬。

4

4 回答 4

3

你有几个问题:

  • 您需要明确关闭 ResultSet。一些驱动程序对于忘记关闭 ResultSet 的容忍度比其他驱动程序少,确保关闭它并没有什么坏处。

  • 你应该捕获 Statement.close 抛出的 SQLException,因为它并不有趣,只是用来掩盖有趣的异常(如果你在这个方法中有东西抛出异常,那么 finally 在退出时抛出异常,你会得到finally 块中的异常并丢失第一个异常)。如果 close 方法调用抛出异常,您确实无能为力,只需记录并继续,这不是值得关注的事情。

  • 你应该放弃在这个方法中处理所有 sqlexception 的想法,statement.executeQuery 抛出的 SQLException 是值得的,如果出现问题应该传播它。您的应用程序中的其他代码可能想知道您的 sql 是否成功,这就是引发异常的原因。

我个人建议为此使用 Ibatis 或 spring-jdbc 之类的库。JDBC 容易出错且乏味,最好利用现有工具。

于 2010-11-16T15:10:54.270 回答
1

以下是我通常处理此类资源的方式(请注意,无论您做什么,都需要stmt != null在致电之前进行检查stmt.close!):

SomeResource resource = null;
try {
    resource = /* ...get the resource... */;

    /* ...use the resource... */

    // Close it    
    resource.close();
    resource = null;

    // ...maybe do some post-processing... */

} catch (SomeException se) {
    // code to handle SomeException
} catch (SomeOtherException soe) {
    // code to handle SomeOtherException
} finally {
    if (resource != null) {
        try {
            resource.close();
        } catch (IOException e) {
        }
    }
}

...虽然我的finally块通常比这简单得多,因为我有实用方法来封装它。(具体来说,它可能看起来像这样:

finally {
    resource = Utils.silentClose(resource);
}

...检查和关闭调用在哪里silentClose屏蔽!null任何异常并始终返回null。)

以上主要方面:

  1. 要么是打开的resource,要么是null从不关闭的!null(除了在close通话和null
  2. 在正常流程中,我close正常调用,没有隐藏它可能抛出的任何异常。
  3. finally子句中,如果resource!null,根据定义,发生了一些异常。因此,我应该尝试关闭resource,但要防止抛出任何异常并掩盖实际出错的事情。

特别是,只要我在主线代码中需要它,我就会保持资源打开,以提高可读性。

还有其他的成语:

  • “重新抛出”习语:始终捕获所有异常,关闭资源,然后重新抛出异常。在我看来会导致很多不必要的代码。
  • “成功标志”成语:设置一个标志——可能是你的返回值——告诉你事情是否有效,然后总是在finally. 问题是,你会得到与我的代码相同的重复,除非你总是要隐藏close. 这使我们:
  • “我不关心关闭时的异常”成语:总是做“静默”关闭。嘎。:-)

将上述内容应用于您的代码:

public static void viewTable(Connection con) throws SQLException {
    Statement stmt = null;
    ResultSet rs   = null; // <== You need this outside the try/catch block
    String query = "select COF_NAME, SUP_ID, PRICE, SALES, TOTAL from " + dbName + ".COFFEES";
    try {
        stmt = con.createStatement();
        rs = stmt.executeQuery(query);
        while (rs.next()) {
            String coffeeName = rs.getString("COF_NAME");
            int supplierID = rs.getInt("SUP_ID");
            float price = rs.getFloat("PRICE");
            int sales = rs.getInt("SALES");
            int total = rs.getInt("TOTAL");
            System.out.println(coffeeName + "\t" + supplierID + "\t" + price + "\t" + sales + "\t" + total);
        }

        // Explicit close, allows for exception since we won't be hiding anything
        rs.close();
        rs = null;
        stmt.close();
        stmt = null;

        // Possible further processing...

    } catch (SQLException e ) {
        JDBCTutorialUtilities.printSQLException(e);
    } finally {
        // Close the ResultSet first, then the Statement
        rs   = Utils.silentClose(rs);
        stmt = Utils.silentClose(stmt);
    }
}
于 2010-11-16T15:05:10.770 回答
1

有很多方法可以编写 JDBC 初始化和关闭以避免样板。但是,要回答您的问题,您可以将 stmt.close() 包装在 try-catch 块中,如下所示。此外,您需要关闭结果集。(未写在下面)您可以考虑使用 SpringDAO 或 Hibernate 而不是 JDBC 来避免检查异常。

public static void viewTable(Connection con) throws SQLException {
    Statement stmt = null;
    String query = "select COF_NAME, SUP_ID, PRICE, SALES, TOTAL from " + dbName + ".COFFEES";
    try {
      stmt = con.createStatement();
      ResultSet rs = stmt.executeQuery(query);
      while (rs.next()) {
        String coffeeName = rs.getString("COF_NAME");
        int supplierID = rs.getInt("SUP_ID");
        float price = rs.getFloat("PRICE");
        int sales = rs.getInt("SALES");
        int total = rs.getInt("TOTAL");
        System.out.println(coffeeName + "\t" + supplierID + "\t" + price + "\t" + sales + "\t" + total);
      }
    } catch (SQLException e ) {
      JDBCTutorialUtilities.printSQLException(e);
    } finally {
      try{
         stmt.close();
      }catch(Exception e) { /*LOG to indicate an issue */}
    }
  }
于 2010-11-16T15:18:59.217 回答
0

就方法而言,它可以做任何事情

 JDBCTutorialUtilities.printSQLException(e);

发生异常时会执行此操作,除非该方法重新引发异常,否则您只会从该方法返回,而不会持久知道发生了异常。

您可以将任何您喜欢的代码放在 Exception 块中。关键问题是如果发生异常,viewTable 的调用者应该做什么。

你大概有代码:

viewTable( /*etc*/);

doSomethingWith( price ); // for example

但是,如果您有例外,那就不好了 - 价格不会被设定。所以要么

一种)。在您的异常块中设置一个标志,然后记得检查它

viewTable( /*etc*/);
if (itAllWorked)
     doSomethingWith( price ); // for example

对我来说,这很容易出错,并且破坏了异常的全部意义。或者

乙)。不要在 viewTable 中捕获异常(除了可能记录它并重新抛出,我想这可能是实用程序方法的用途)。

try {

       viewTable()
       doSomethingWith(price):
       // all the normal flow
} catch (SqlException e) {
        //some reasnable action, which does not depend on things like proce
}
于 2010-11-16T15:12:10.257 回答