【问题标题】:How can I handle this exception within this method?如何在此方法中处理此异常?
【发布时间】:2010-11-16 14:56:35
【问题描述】:

我的 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。我不想这样做,因为我希望在这个类中处理任何问题。但是,我确实希望在 finally 块中调用 Statement#close(),以便它始终关闭。

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

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

【问题讨论】:

  • 这很模棱两可。 My problem with this way of handling to connection is that it closes the statement in the finally block and the method throws any SQLException that may occur. I don't want to do that, because I want any problems handled within this class. However, I do want that Statement#close() call in the finally block so that it is always closed.
  • 该代码捕获所有 SQL 异常,除了(可能)从.close() 调用返回的一个异常(如果它甚至可以抛出一个)。我认为该代码已经非常接近您想要的,但是您的问题并不是很清楚。

标签: java jdbc sqlexception


【解决方案1】:

你有几个问题:

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

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

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

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

【讨论】:

  • 那么您是否建议我只将 stat.close() 调用放在 try/catch 块中,并让该方法在其他任何地方抛出错误?
  • 是的,stmt.close(和 resultSet.close)应该在各自的 finally 块中都有一个 try/catch。
  • +1 表示“如果关闭方法调用引发异常,您真的无能为力,只需记录并继续”
【解决方案2】:

有很多方法可以编写 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 */}
    }
  }

【讨论】:

  • 这样写算是干净的代码吗?正如我在编辑中所写,嵌套 try/catch 似乎很尴尬。
  • @Michael:是的,可以嵌套 try 块,对我来说,它比 Crowder 的方法更可取,因为不需要空检查。如果在这个例子中没有足够的嵌套,stmt 应该在关闭它的块之外初始化。
【解决方案3】:

这就是我通常如何处理这类资源问题(请注意,无论你做什么,都需要在调用stmt.close之前检查stmt != null!):

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

【讨论】:

  • 为什么资源在两个地方都关闭了? finally 将始终运行。
  • @djna:我只是添加了一个解释。基本上,因为在一个地方(主线代码)我希望close 能够抛出异常,但在异常处理情况下,我想异常而不是隐藏异常这首先引发了问题。
【解决方案4】:

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

 JDBCTutorialUtilities.printSQLException(e);

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

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

你大概有代码:

viewTable( /*etc*/);

doSomethingWith( price ); // for example

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

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

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

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

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

try {

       viewTable()
       doSomethingWith(price):
       // all the normal flow
} catch (SqlException e) {
        //some reasnable action, which does not depend on things like proce
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-07-25
    • 2011-04-23
    • 1970-01-01
    • 2014-02-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多