web-dev-qa-db-ja.com

どこで閉じるJava PreparedStatements and ResultSets?

コードを考慮してください:

_PreparedStatement ps = null;
ResultSet rs = null;
try {
  ps = conn.createStatement(myQueryString);
  rs = ps.executeQuery();
  // process the results...
} catch (Java.sql.SQLException e) {
  log.error("an error!", e);
  throw new MyAppException("I'm sorry. Your query did not work.");
} finally {
  ps.close();
  rs.close();
}
_

PreparedStatement.close()ResultSet.close()の両方が_Java.sql.SQLException_をスローするため、上記はコンパイルされません。それでは、finally節にtry/catchブロックを追加しますか?または、closeステートメントをtry句に移動しますか?または、単に近くに電話することを気にしませんか?

35
MCS

ファイルI/Oの場合、通常はtry/catchをfinallyブロックに追加します。ただし、finallyブロックから例外をスローしないように注意する必要があります。例外があると、元の例外(存在する場合)が失われます。

データベース接続のクローズのより具体的な例については、 この記事 を参照してください。

9
Michael Myers

Java 7では、明示的に閉じるべきではありませんが、 自動リソース管理 を使用して resources が閉じられ、例外が適切に処理されるようにします例外処理は次のように機能します。

トライの例外|近くの例外|結果
 ----------------- + -------------------- + ----- ----------------------------------- 
いいえ|いいえ|通常どおり続行します
いいえ|はい| close()例外をスローします
はい|いいえ| tryブロックから例外をスローする
はい|はい|メイン例外にclose()例外を追加します
 | | 「抑制」として、メイン例外をスローします

うまくいけば、それは理にかなっています。次のようなきれいなコードが許可されます。

_private void doEverythingInOneSillyMethod(String key)
  throws MyAppException
{
  try (Connection db = ds.getConnection()) {
    db.setReadOnly(true);
    ...
    try (PreparedStatement ps = db.prepareStatement(...)) {
      ps.setString(1, key);
      ...
      try (ResultSet rs = ps.executeQuery()) {
        ...
      }
    }
  } catch (SQLException ex) {
    throw new MyAppException("Query failed.", ex);
  }
}
_

Java 7より前では、nullの参照をテストするのではなく、ネストされたfinallyブロックを使用するのが最善です。

ここで示す例は、ネストが深いと見苦しいかもしれませんが、実際には、適切に設計されたコードでは、同じメソッドで接続、ステートメント、結果を作成することはできません。多くの場合、ネストの各レベルでは、リソースを別のメソッドに渡し、別のリソースのファクトリとして使用します。このアプローチでは、close()からの例外は、tryブロック内からの例外をマスクします。これは克服できますが、コードがさらに複雑になり、Java 7に存在する「抑制された」例外チェーンを提供するカスタム例外クラスが必要になります。

_Connection db = ds.getConnection();
try {
  PreparedStatement ps = ...;
  try {
    ResultSet rs = ...
    try {
      ...
    }
    finally {
      rs.close();
    }
  } 
  finally {
    ps.close();
  }
} 
finally {
  db.close();
}
_
45
erickson

独自のjdbcを実際に手で回している場合は、間違いなく面倒です。最後のclose()は、独自のtryキャッチでラップする必要がありますが、これは少なくともtheいです。クローズをスキップすることはできませんが、接続がクローズされるとリソースがクリアされます(プールを使用している場合は、すぐに終了しない場合があります)。実際、フレームワーク(たとえば、休止状態)を使用してdbアクセスを管理することの主なセールスポイントの1つは、接続と結果セットの処理を管理して、閉じることを忘れないようにすることです。

このような単純なことができます。これにより、少なくとも混乱が隠され、何かを忘れないことが保証されます。

public static void close(ResultSet rs, Statement ps, Connection conn)
{
    if (rs!=null)
    {
        try
        {
            rs.close();

        }
        catch(SQLException e)
        {
            logger.error("The result set cannot be closed.", e);
        }
    }
    if (ps != null)
    {
        try
        {
            ps.close();
        } catch (SQLException e)
        {
            logger.error("The statement cannot be closed.", e);
        }
    }
    if (conn != null)
    {
        try
        {
            conn.close();
        } catch (SQLException e)
        {
            logger.error("The data source connection cannot be closed.", e);
        }
    }

}

その後、

finally {
    close(rs, ps, null); 
}
25
Steve B.

低レベルの例外管理のコーディングに時間を無駄にしないでください。Spring-JDBCのような高レベルのAPI、またはconnection/statement/rsオブジェクトのカスタムラッパーを使用して、厄介なtry-catchに乗ったコードを隠してください。

7
BraveSirFoobar

私は通常、このようなことを閉じることができるユーティリティメソッドを持っています。これには、null参照で何かをしようとしないように注意することも含まれます。

通常、close()が例外をスローする場合、実際には気にしないので、例外をログに記録して飲み込むだけです。しかし、別の方法として、RuntimeExceptionに変換することもできます。どちらの方法でも、呼び出しが簡単なユーティリティメソッドで行うことをお勧めします。多くの場所でこれを行う必要があります。

PreparedStatementのクローズに失敗した場合、現在のソリューションはResultSetをクローズしないことに注意してください-ネストされたfinallyブロックを使用することをお勧めします。

5
Jon Skeet

Java 7を使用している場合は、 AutoCloseable (つまりPreparedStatementResultset

また、この質問が興味深いと思うかもしれません: Closing ResultSet in Java 7

2
Guido

これは古い質問ですが、誰かが答えを探している場合に備えて、Javaにtry-with-resouceソリューションがあります。

static String readFirstLineFromFile(String path) throws IOException {
      try (BufferedReader br =
                   new BufferedReader(new FileReader(path))) {
        return br.readLine();
    }
}
1
Xin

おそらく物事を行うための古い(しかしシンプルな)方法ですが、それでも動作します:

public class DatabaseTest {

    private Connection conn;    
    private Statement st;   
    private ResultSet rs;
    private PreparedStatement ps;

    public DatabaseTest() {
        // if needed
    }

    public String getSomethingFromDatabase(...) {
        String something = null;

        // code here

        try {
            // code here

        } catch(SQLException se) {
            se.printStackTrace();

        } finally { // will always execute even after a return statement
            closeDatabaseResources();
        }

        return something;
    }

    private void closeDatabaseResources() {
        try {
            if(conn != null) {
                System.out.println("conn closed");
                conn.close();
            }

            if(st != null) {
                System.out.println("st closed");
                st.close();
            }

            if(rs != null) {
                System.out.println("rs closed");
                rs.close();
            }

            if(ps != null) {
                System.out.println("ps closed");
                ps.close();
            }

        } catch(SQLException se) {
            se.printStackTrace();
        }               
    }
}
0
silver

@ericksonの答えに基づいて、なぜこのような1つのtryブロックでそれをしないのですか?

private void doEverythingInOneSillyMethod(String key) throws MyAppException
{
  try (Connection db = ds.getConnection();
       PreparedStatement ps = db.prepareStatement(...)) {

    db.setReadOnly(true);
    ps.setString(1, key);
    ResultSet rs = ps.executeQuery()
    ...
  } catch (SQLException ex) {
    throw new MyAppException("Query failed.", ex);
  }
}

ResultSetオブジェクトが閉じられるとtryが自動的に閉じられるため、ResultSetブロック内にPreparedStatementオブジェクトを作成する必要がないことに注意してください。

ResultSetオブジェクトは、それを生成したStatementオブジェクトが閉じられるか、再実行されるか、複数の結果のシーケンスから次の結果を取得するために使用されると、自動的に閉じられます。

リファレンス: https://docs.Oracle.com/javase/7/docs/api/Java/sql/ResultSet.html

0
Catfish

Closeの呼び出しを省略しないでください。問題を引き起こす可能性があります。

最終的にtry/catchブロックを追加することを好みます。

0
asalamon74