web-dev-qa-db-ja.com

マルチスレッドシステムで静的なJava.sql.Connectionインスタンスを使用しても安全ですか?

TomcatでWebアプリケーションを実行しています。すべてのDBクエリを処理するクラスがあります。このクラスには、Connectionオブジェクトとクエリ結果を返すメソッドが含まれます。

これは接続オブジェクトです。

private static Connection conn = null;

インスタンスは1つ(シングルトン)のみです。

さらに、db内のユーザーの検索など、クエリを実行するメソッドがあります。

public static ResultSet searchUser(String user, String pass) throws SQLException

このメソッドは、静的Connectionオブジェクトを使用します。私の質問は、静的Connectionオブジェクトスレッドセーフでの使用ですか?または、多くのユーザーがsearchUserメソッドを呼び出すときに問題が発生する可能性がありますか?

48
Asher Saban

静的Connectionオブジェクトスレッドセーフでの使用は安全ですか?

絶対違う!

この方法では、すべてのユーザーが送信するすべてのリクエスト間で接続が共有されるため、すべてのクエリが互いに干渉します。ただし、スレッドセーフが唯一の問題ではなく、リソースリークも他の問題です。アプリケーションの存続期間中、1つの接続を開いたままにします。平均的なデータベースは、DBの構成に応じて、通常30分から8時間の間、長時間開いている接続を再利用します。そのため、Webアプリケーションがそれより長く実行されると、接続が失われ、クエリを実行できなくなります。

この問題は、それらのリソースが複数回再利用されるクラスインスタンスのstaticインスタンス変数として保持されている場合にも適用されます。

常に接続、ステートメント、および結果セットを取得して閉じます可能な限り短いスコープ、できればまったく同じ try-with-resourcesブロック内 次のJDBCイディオムに従ってクエリを実行する場所として:

public User find(String username, String password) throws SQLException {
    User user = null;

    try (
        Connection connection = dataSource.getConnection();
        PreparedStatement statement = connection.prepareStatement("SELECT id, username, email FROM user WHERE username=? AND password=md5(?)");
    ) {
        statement.setString(1, username);
        statement.setString(2, password);

        try (ResultSet resultSet = statement.executeQuery()) {
            if (resultSet.next()) {
                user = new User();
                user.setId(resultSet.getLong("id"));
                user.setUsername(resultSet.getString("username"));
                user.setEmail(resultSet.getString("email"));
            }
        }
    }       

    return user;
}

ここでResultSetnotで返す必要があることに注意してください。 ResultSetを安全に閉じることができるように、すぐにそれを読み取り、非JDBCクラスにマップしてから返す必要があります。

Java 7をまだ使用していない場合は、try-finallyブロックを使用します。このブロックでは、閉じたリソースを取得したときと逆の順序で手動で閉じます。例: JDBCでConnection、Statement、ResultSetを閉じる頻度はどれくらいですか?

接続のパフォーマンスが心配な場合は、代わりに接続プーリングを使用する必要があります。これは、多くのJava EEアプリケーションサーバーに組み込まれています。Tomcatのようなベアボーンサー​​ブレットコンテナーでさえもサポートします。サーバー自体にJNDIデータソースを作成し、WebアプリケーションにDataSource。既に透過的に接続プールになっています。以下のリストの最初のリンクで例を見つけることができます。

こちらもご覧ください:

76
BalusC

Selectクエリのみを実行している場合(searchUserはデータ選択のみのように聞こえます)、スレッドの競合を除き、問題はありません。

私の知る限り、Connectionは一度に1つのクエリしか処理できないため、単一のインスタンスを使用することで、データベースアクセスを本質的にシリアル化できます。しかし、これは必ずしも意味するものではなく、マルチスレッド環境でこのようなデータベースにアクセスすることは常にsafeです。同時アクセスがインターリーブされている場合、依然として問題がある可能性があります。

1
TPete