1

jdbcに関するいくつかの同時実行の問題についてアドバイスが必要です。基本的には、値を更新してから、更新を使用してその値を取得し、次に選択する必要があります。自動コミットをオフにすることで、他のトランザクションがこのテーブルにアクセスできないため、他のトランザクションこれがコミットされるまで、更新および選択クエリを実行することはできません。

以下はサンプルコードです。これはうまくいくと思いますか?これを実装するためのより良い解決策は他にありますか?

int newVal=-1;
con.setAutoCommit(false);
PreparedStatement statement = con.prepareStatement("UPDATE atable SET val=val+1 WHERE id=?");
statement.setInt(1, id);
int result = statement.executeUpdate();
if (result != 1) {
    throw new SQLException("Nothing updated");
} else {
    statement = con.prepareStatement("SELECT val FROM atable WHERE id=?");
    statement.setInt(1, id);
    ResultSet resultSet = statement.executeQuery();
    if (resultSet.next()) {
        newVal =  resultSet.getInt("val");
    }
}
statement.close();
con.commit();
con.setAutoCommit(true);

ありがとう。

4

2 に答える 2

10

何らかの形式のデータ ソースを使用すると仮定すると、トランザクション性と分離レベルが必要な場合は、そこで構成できます。しかし、明確にするために:

try(Connection con = ds.getConnection()){
   con.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);
   con.setAutoCommit(false);
   //...
} catch(SQLException sqle) {
   throw new MyModelException(e)
}

これで、テーブルのバージョン (またはタイムスタンプ) フィールドを更新することで、悲観的ロックをトリガーできます。これにより、データベースのロックがトリガーされます (ほとんどの場合、レコード レベルで)。

try(PreparedStatement pStm = con.prepareStatement("update atable set version=version+1")){
   pStm.executeUpdate();
}

この時点で、別のユーザーが同じレコードを同時に更新しようとすると、この接続は待機するかタイムアウトになるため、両方の準備ができている必要があります。トランザクションが終了する (コミットまたはロールバック) まで、レコードはロック解除されません。

次に、必要なものを安全に選択して更新し、データを処理するときに他の人がレコードに触れないようにすることができます. 他の誰かが試みた場合、終了するまで待機します (または、接続構成によってはタイムアウトします)。

または、楽観的ロックを使用することもできます。この場合、レコードを読み取り、変更を行いますが、更新では、バージョン/タイムスタンプ フィールドが最初に読み取ったものと同じであることを確認して、読み取り後に他の誰も変更していないことを確認します。この場合、古い/古いデータがあることに気付いた場合は、トランザクションを再試行する (またはすべてを中止する) 準備をしておく必要があります。

すなわちupdate atable set afield=? where id=? and version=1

影響を受ける行数が 0 の場合、読み取りと更新の間にレコードが更新され、レコードがバージョン 1 ではなくなった可能性が高いことがわかります。

于 2013-01-10T10:50:25.440 に答える
3

接続を設定autocommit=falseしても、他の接続/スレッドがデータベース内の行を変更するのを防ぐことはできません! その特定の接続での各 JDBC 操作の後にのみ、自動コミットを無効にします。

行をロックする必要があります。行に対する他のトランザクションをselect ... for update防ぐために、また、単一のトランザクション内で選択と更新を行う必要があります。

乾杯、

于 2013-01-10T10:27:16.020 に答える