web-dev-qa-db-ja.com

Tryステートメントの「Finally」ブロックでデータベース接続を閉じることに問題はありますか?

アプリケーションのリファクタリングを行っており、ISOスキャン(HP Fortifyに基づく静的コード分析ツール)で報告される問題の数を減らすようにしています。現在、私が対処しようとしているのは、アプリケーションが構築した「未リリースのリソース:データベース」の問題です。これの最も顕著な理由の1つは、次のような構成です。

Connection conn = null;
Boolean myConn = false;
try{
   if(conn == null){
     conn = DatabaseUtil.getConnection();
     myConn = true;
   }
   result = DbClass.getObject(conn, otherParameters);
}catch(DatabaseException de){
  throw de;
}catch(SQLException sqle){
  throw new DatabaseException("Error Message");
}finally{
  if(myConn && conn != null){
    try{
      conn.rollback();conn.close();
    }catch(SQLException sqle){}
}

これは、アプリケーションのかなり標準的な構成であり、Tryブロックの最後にデータベース接続が確実に閉じられるようにします。しかし、使用するたびに、ツールはそれを問題として報告します。

このコンストラクトに誤りはありますか?問題である場合、それを修正するために私たちができることはありますか?

7
Zibbobz

そして、ツールは正しいです!

分析の(可能性のある)制限とは別に、_(conn != null) == myConn_を理解する場合としない場合があります。rollbackSqlExceptionをスローすることを確認してください。存在する場合、closeが呼び出されることはありませんです。これは、正確にリソースリークの定義です。 current DB、ドライバ、およびプールライブラリがrollbackをスローした場合、接続はすでに解放されていますが、これは一般的なケースでは当てはまりません。したがって、潜在的な問題です。

コメントに記載されているように、Java 7を使用している場合、_try-with-resources_を使用することにより、この構成の多くを簡略化できます。また、 tryブロックのclose()メソッドでのロールバック:

_@Override
public void close() throws SQLException {
    try {
        rollback();
    catch (...) {
        //Do stuff
    finally {
        super.close();
    }
}
_

ここでは、rollbackが何をする場合でも、close()が実際に実行されることが100%保証されます。

レガシーソフトウェアを使用している場合は、上記のコードを直接tryに配置できます。

_Connection conn = null;
try {
   conn = DatabaseUtil.getConnection();
   result = DbClass.getObject(conn, otherParameters);
} catch(SQLException sqle) {
  throw new DatabaseException("Error Message");
} finally {
  if (conn != null) {
    try{
        conn.rollback();
    } catch(SQLException sqle) {
        //try-with-resources would add this to suppressed exceptions, you can either log or rethrow
    } finally {
        conn.close();
    }
}
_
10
Ordous

リソースのリリースはブール変数の状態を条件とし、別のtryブロックに入れられるため、静的アナライザーはrollback()およびclose()が実行が保証されています。

getConnection()ファクトリメソッドによって返されたタイプのConnectionオブジェクトのドキュメントを確認し、メソッドrollback()およびclose()が例外をスローします。そうしない場合、これらのメソッドを呼び出すときに、追加のtrycatchブロックは必要ありません。

ただし、最初に、開いている接続を通知するために使用しているブール変数を削除します。必要ありません。接続の確立に成功した場合を除き、getConnection()ファクトリメソッドから接続オブジェクトを取得しないでください。

Fortifyのセキュリティレポートは次のコード構造を示唆していることに注意してください:

public void execCxnSql(Connection conn) {
    Statement stmt;
    try {
        stmt = conn.createStatement();
        ResultSet rs = stmt.executeQuery(CXN_SQL);
        ...
    } 
    finally {
        if (stmt != null) {
            safeClose(stmt);
        }
    }
}
public static void safeClose(Statement stmt) {
    if (stmt != null) {
        try {
            stmt.close();
        } catch (SQLException e) {
            log(e);
        }
    }
}

...これは、ブール変数が問題であり、追加のtryブロックではないことを示唆しています。

5
Robert Harvey

コンパイラーはコードをまったく実行できなかったので、条件を取り除きます。

次に、ExceptionではなくSQLExceptionをキャッチして、接続が初期化されていない場合にNullPointerExceptionをキャッチします。 SybaseのEAServerペーパーで読んだ推奨事項と、おそらくJavaのガベージコレクターとは関係なく、EAServerの接続プールとは関係がないため、通常はオブジェクトを閉じた後でnullに設定します。おそらく"貨物養殖」ここに。

Connection conn = null;
Boolean myConn = false;
try{
   if(conn == null){
     conn = DatabaseUtil.getConnection();
     // myConn = true; no need for this
   }
   result = DbClass.getObject(conn, otherParameters);
}catch(DatabaseException de){
  throw de;
}catch(SQLException sqle){
  throw new DatabaseException("Error Message");
}finally{
    try{
      conn.rollback();
      conn.close();
      conn=null; // good practice according to people at Sybase
    }catch(Exception e){
         //fails silently when conn is not initialized.
    }
}
0