2

Java EE Webアプリケーションは、iBatis(ORM)を使用してデータベース操作を実行します。データベースの運用フローは以下のとおりです。

フロー:JSP ---> Action ---> ServiceIMpl ---> DaoImpl---->IBatisを介した更新クエリの呼び出し

:アクション、サービス、およびDAOクラスは、Spring2.5の依存性注入手法を使用してインスタンス化されます。Struts2を使用しています。

問題:2人の同時ユーザーが同じレコードを検索し、User1がAttribute1を更新し、User2がAttribute2を更新します。User1は最初に[保存]をクリックし、次にUser2は[保存]をクリックします(数秒の差で互いにフォローします)。データベースにデータが表示されている場合、User1の更新のみが存在します。[監査]列には、User1の更新のみが表示されます。Attribute2でのUser2の更新は行われず、同時に、例外はスローされません。

sqlmapxmlで定義された更新クエリの呼び出しを取り巻くiBatisでbeginトランザクションとcommitトランザクションを使用してみました。同じ問題が解決しません。これらのトランザクションコマンドも削除しようとしましたが、問題は解決しません。

更新中の同時実行を処理するために、特別な/異なることを行う必要がありますか?iBatisのようなORMは自分で処理しませんか?

追加情報:さらに調査したところ、次の情報が明らかになりました。

User1とUser2がレコードをクリックすると、その完全なデータがフェッチされて表示されます。

これで、User1とUser2の両方がいくつかの属性を変更し、[保存]をクリックすると(同時に)、最初にUser1のデータが更新され、次にUser2のデータが更新されている間に、User1の既に更新されたデータが上書きされて失われます。

What approach is usually followed in such screens to handle such scenarios ?

4

2 に答える 2

9

問題はトランザクションではありません。何かを変更する前に、最後に読んだときに誰も変更していないことを確認する必要があります。

Webアプリケーションでは、楽観的ロックを使用する必要があります。基本的に、テーブルには「バージョン」フィールドがあり、初期値はinsert(通常は1)であり、ごとに増分されますupdate。手順は次のとおりです。

  1. バージョンフィールドを含むテーブルからデータを読み取ります

  2. バージョンフィールドを含むデータをクライアント側に送信します

  3. 変更されていないバージョンフィールドを含む、変更されたデータをクライアントから受信します

  4. テーブルを更新してバージョンをインクリメントしますが、行のバージョンフィールドがクライアントから受け取った元のフィールドと同じである場合に限ります。つまり、あなたが持っていた場所

    update
    ... 
    where id = :id_from_client
    

    、今あなたはする必要があります

    update 
    ..., 
    version = version + 1 
    where 
    id = :id_from_client and 
    version = :version_from_client
    
  5. 更新された行の数を取得します(通常、この情報は永続フレームワークの更新操作から直接取得できます)。更新された行数が1の場合、操作は成功しました。更新された行の数が0の場合、同時実行エラーを通知する必要があります。

例:

  1. User1は、バージョン=17のデータを取得します

  2. User2は、バージョン=17のデータを取得します

  3. User1アトミック(アトミック性は単一のSQLステートメントであることに由来します):

    • バージョン=17かどうかを確認します
    • データを更新します
    • バージョンを18に設定します

  4. User2アトミック:

    • バージョン=17かどうかを確認します
    • バージョンは実際には18であったため、同時実行エラーを通知して操作を中止し、他の誰かが同じデータを変更したために操作を完了できなかったことをユーザーに通知します。

現在のほとんどの永続性フレームワークは、これをすぐにサポートし、ほとんど自動的にサポートします(たとえば、JPA @Versionアノテーションを参照してください)。iBatisにはないようですが、SQLに非常に近いため、自分で実装する際に多くの問題が発生することはありません。iBatisを使用した楽観的ロックの詳細については、このブログエントリを参照してください。

このスキームでは、更新が失われることはありません。唯一の問題は、特定のケースでユーザーが非常に煩わしいことです。5分間かけてフォームに入力すると、最後に操作を完了できなかったことが通知されます。最初からやり直す必要があります!! これらのいくつかのケースでは、楽観的ロックに加えて、より洗練されたものを実装する必要があります。ある種のリモートリソースリース(リンク??)を使用します:

  1. クライアントは、サーバーへのデータのリースを要求します。

  2. データがすでに別のクライアントにリースされている場合は、同時実行エラーを通知します。

  3. それ以外の場合は、有限の期間、クライアントにリースを付与します。

  4. リースの有効期限が切れる前に、サーバーはクライアントにデータへの排他的アクセスを許可します。クライアントは、リースの更新を要求することもできます(たとえば、AJAXを介して)。サーバーは更新を受け入れるか拒否することができます。(リース時間と更新ポリシーは、処理されている特定のユースケースに応じてサーバーが決定する必要があります)。

  5. リースの有効期限が切れます(これは、たとえばサーバーでスケジュールされたタスクを使用して実行できます)。リースの有効期限が切れると、データは「ロック解除されている」と見なされ、クライアントに新しいリースが付与されない限り、サーバーはクライアントへのアクセスを拒否する必要があります。

このようにして、ユーザーは、実行しようとしていた時間のかかる操作を開始する前に、誰かが同じデータを変更していることを通知できます。

于 2012-09-27T20:14:27.133 に答える
1

Ibatisはロックを実行しません。シナリオを見ると楽観的ロックで問題を解決できます。楽観的ロックを実現するには、オプションがあります。1.org.springframework.jdbc.datasourceのようなトランザクション管理をサポートするフレームワークを使用します。DataSourceTransactionManager。これらは伝播動作と分離レベルを提供します。ただし、使用するデータソースは伝播レベルと分離レベルをサポートする必要があります。Springは次の分離レベルを提供します。.ISOLATION_DEFAULT .ISOLATION_READ_UNCOMMITTED .ISOLATION_READ_COMMITTED.ISOLATION_REPEATABLE_READ.ISOLATION_SERIALIZABLE。2.次に、楽観的ロックの実装を提供できます。

于 2013-02-18T10:54:15.650 に答える