2012-04-04 13 views
2

Javaアプリケーションで、アトミックにアブノーマルなDBステートメントのセットを実行する要件があります。&たとえば、アプリケーションはある表からデータ行を読み取り、別の表のデータ行を更新する必要があります。JavaでDB操作のセットをロックする

QueryRunner queryRunner = new QueryRunner(); // DBUtils query runner 

Object[] params = new Object[] { param }; 

Connection conn = null; 
try { 
    conn = ...; // get connection 
    conn.setAutoCommit(false); 
    result = queryRunner.query(conn, "select x, y, z from table1 where column1 = ?", new BeanHandler<SomeBean>(SomeBean.class), params); 

    // logic to get value for update 

    queryRunner.update(conn, "update table2 set p = ? where q = ?", some_value, some_id); 
    conn.commit(); 
} catch (SQLException e) { 
    // 
} finally { 
    DBUtils.closeQuietly(conn); 
} 

上記のように、自動コミットを接続に対してfalseに設定し、後で明示的にコミットすることで、トランザクション管理が実現します。しかし、上記のコードはマルチスレッド環境でも実行することができます。また、2つのDB文(&を選択)を相互排他的に実行する必要があります。

私は、以下に示すように、その方法で共有Java Lockオブジェクトを使用することを考えています。クラスで

、方法で

private Lock lock = new ReentrantLock(); // member variable 

lock.lock(); 
try { 
    conn = ...; // get connection 
    conn.setAutoCommit(false); 

    result = queryRunner.query(conn, "select x, y, z from table1 where column1 = ?", new BeanHandler<SomeBean>(SomeBean.class), params); 

    // logic to get value for update 

    queryRunner.update(conn, "update table2 set p = ? where q = ?", some_value, some_id); 
    conn.commit(); 
} finally { 
    DBUtils.closeQuietly(conn); 
    lock.unlock(); 
} 

それは一種の問題を解決することができるようです。しかし、これがベストプラクティスであり、このためのより良い代替手段(フレームワークなど)があるのだろうかと思いますか?

答えて

1

私の提案では、データベースでアプリケーションの代わりにそれらのロックを管理することをお勧めします。これは、複数のJVMがコードを実行している場合を処理します。あなたが言及したロック機構は、単一のJVMでのみ有効です。

これを達成する方法はSELECT ... FOR UPDATEです。これにより、選択した行にロックが設定され、トランザクションがコミットまたはロールバックされたときにロックが解除されます。これは、テーブルレベルのロックよりも優れています。なぜなら、これらの行は、現在の値を読み取っても更新しないだけの他のトランザクションによって読み取られる可能性があるからです。別のトランザクションがFOR UPDATEロックを取得しようとすると、最初のトランザクションが終了するまでブロックされます。

+0

ありがとう、Hiro2k。私たちのアプリケーションはサーブレットコンテナで実行されており、単一のJVM内にあると私は信じている。 SELECT文が動作しているテーブル以外のテーブルの行を更新しているので、 'SELECT ... FOR UPDATE'が私のケースで動作しないかもしれません。 – Chen

+0

そうですが、ロードバランシングやスケーリングのためにサーバーの別のインスタンスを追加することに決めた場合、この問題が発生します。 2番目のテーブルの行に 'SELECT ... FOR UPDATE'をメソッドの先頭に追加すると、他のテーブルは終了するまで待たなければなりません。 – Hiro2k

+0

良い点、@ Hiro2k。私は私の使用事例を評価して、それらのすべてがカバーできるかどうかを見ていきます。 – Chen

1

あなたが必要とするアトミックを達成する唯一の方法は、データベース内のストアドプロシージャを使用してデータを分離し、一度にロックすることです。 Javaレベルでのロックでは、データベース内のロックを簡単に行うことはできません。

+0

私の提案は、関連するデータが単一のJVM内の同じオブジェクトのメソッド呼び出しによって更新される場合にのみ機能します。それはデータベースレベル自体にロックをかけません。つまり、他のメソッド呼び出しや他のアプリケーションなど、別の場所でトリガされた同じデータに対して予期しない他の更新が行われることを防ぐことはできません。これはストアドプロシージャを利用することで軽減できますか? – Chen

1

このような問題を処理する別の方法は、すべてのデータベーストランザクションにシリアライズ可能なトランザクション分離レベルを使用することです。これにより、一組のトランザクションは、実際に一度に1つずつ実行されることなく、一度に1つずつ実行されるように動作します。これを行う場合、直列化失敗を捕捉するフレームワーク(SQLState 40001)を使用してトランザクションを再試行する必要があります。大きな相乗効果は、トランザクション間の特定の相互作用について心配する必要がないことです。トランザクションが実行される唯一の場合にトランザクションが正しいことを行う場合、トランザクションミックスで正しいことが実行されます。

このようにすべてのトランザクションをシリアライズする必要があることに注意してください。

0

私の理解から、選択と更新ステートメントを含むコードブロックをスレッドセーフとして作成したいのですか?それはsynchronizedキーワードが使われているものです。この質問は、私がちょうどここでそれを書きたいと思っています。それらのコード行を同期ブロックの下に置きます。

関連する問題