0

Java JDBC チュートリアルの次のような 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、例外がクラス内で処理されるように返されたフィールドの 1 つを返します。これを処理する別の、おそらくより良い方法はありますか?

編集: close()SQLException は私が懸念しているものです。可能であれば、メソッド内で処理したいと思います。finally に try/catch を書くこともできますが、それは非常に厄介です。

4

4 に答える 4

3

いくつかの問題があります。

  • ResultSet を明示的に閉じる必要があります。一部のドライバーは、他のドライバーよりも ResultSet を閉じるのを忘れることを許容しませんが、確実に閉じても問題はありません。

  • Statement.close によってスローされた SQLException をキャッチする必要があります。これは興味深いものではなく、興味深い例外をマスクするためだけに機能するためです (このメソッドで例外をスローする何かがある場合、最終的に途中で例外をスローすると、次のようになります)。 finally ブロックからの例外が発生し、最初の例外が失われます)。close メソッドの呼び出しで例外がスローされた場合、実際にできることは何もありません。ログに記録して続行するだけです。心配する必要はありません。

  • このメソッドですべての sqlexceptions を処理するという考えをあきらめる必要があります。statement.executeQuery によってスローされる SQLException は価値のあるものであり、何か問題が発生した場合に伝播する必要があります。アプリケーションの他のコードは、SQL がここで成功したかどうかを知りたがる可能性が高く、それが例外をスローするためのものです。

個人的には、これには Ibatis や spring-jdbc などのライブラリを使用することをお勧めします。JDBC はエラーが発生しやすく面倒なので、既存のツールを利用することをお勧めします。

于 2010-11-16T15:10:54.270 に答える
1

定型文を回避するために JDBC の初期化と終了を記述する方法は多数あります。ただし、質問に答えるには、以下のように stmt.close() を try-catch ブロックでラップできます。また、結果セットを閉じる必要があります。(以下には書かれていません) チェック例外を回避するために、JDBC の代わりに SpringDAO または Hibernate を検討することができます。

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 に答える
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);
}

...どこでsilentClosecheck!nullと close を呼び出して例外をマスクし、常に を返しますnull。)

上記の重要な側面:

  1. resource開いているかnull、閉じているだけです (呼び出しと の!null間の短い時間を除いて; これを他のスレッドと共有しないと仮定しています)。closenull
  2. 通常のフローでは、closeスローされる可能性のある例外を非表示にせずに、通常どおり呼び出します。
  3. finally節の if isresourceでは!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 に答える
0

メソッドの現状として、それは何でもします

 JDBCTutorialUtilities.printSQLException(e);

そのメソッドが例外を再スローしない限り、例外が発生したときに実行します。例外が発生したという永続的な知識がないまま、メソッドから戻るだけです。

好きなコードを Exception ブロックに入れることができます。重要な問題は、例外が発生した場合に viewTable の呼び出し元が何をすべきかということです。

おそらくコードがあります:

viewTable( /*etc*/);

doSomethingWith( price ); // for example

しかし、例外があった場合、それは良くありません - 価格は設定されません. だからどちらか

a)。例外ブロックでフラグを設定し、それをチェックすることを忘れないでください

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
}
于 2010-11-16T15:12:10.257 に答える