コントローラロジック:
def updateObject() {
Object o = Object.get(params.id as Long)
o.otherObjects.clear()
objectDataService.saveObject(o.id)
OtherObject newObject = new OtherObject;
o.addToOtherObjects(newObject)
objectDataService.saveObject(o.id)
}
ServiceLogic
def saveObject(long profileId) {
o.save(flush:true)
}
どうなるか
90%の場合、これは問題なく機能します。
問題
ERROR errors.GrailsExceptionResolver - StaleObjectStateException occurred when processing request: [GET] /controller/updateObject - parameters:
stuff[]: data
Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [com.path.Object#1].
Stacktrace follows:
Message: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [com.path.Object#1]
関連する質問を読み、上記のmerge
呼び出しを見つけました。ケースの約50%を解決しましたが、すべてではありません。
私たちにとって、いくつかの異なるアプローチが最終的にStaleObjectExceptionが定期的に発生するのを解決しました。
object = object.refresh()
オブジェクトを取得した後にオブジェクトを更新すると、ほとんどのStaleObjectExceptionsが解決されました。特に、誰かが別の場所から同じオブジェクトを操作し、そのメンバーの一部を変更した可能性がある場合(ほとんどの問題はコレクションメンバーに発生しました)。
プロジェクト全体の安定性:
wrongly linked resources
実際には必要のない特定のリソースファイルに404があったため、しばらくの間無視していました。ファイルが欠落していると、セッションが開いたままになり、StaleObjectsが左右に作成されることがわかりました。
したがって、直面している人へのヒントとして通常よりも(一部のStaleObjectsは常に発生する可能性があります-上記の回答を参照してください)StaleObjectExceptions:allリソースが正しくリンクされていることと開発者ツールを確認してください(Chrome F12)不足しているファイルを報告しません。
StaleObjectStateException:
この例外についてすでにコメントしているように。フラッシュ中(明示的または暗黙的)に無効なバージョン管理されたドメインオブジェクトがセッションにある場合、バージョン番号はバンプされますが、データベースには保持されません。次に、それが再び有効になり、保存およびフラッシュされると、バージョン番号がデータベース内のバージョンと一致せず、StaleObjectStateExceptionがスローされるため、hibernateはそれが古いと見なします。
これは次のように解決できます。
- 更新を行う前にバージョン値を確認する必要があります。バージョン値が1の場合は、プログラムを使用して更新する必要があります。更新の特定の操作で。
- @versionアノテーションを使用する。
@versionについて:
楽観的ロックのバージョン番号メカニズムは、@ Versionアノテーションを介して提供されます。例:@Versionアノテーション
@Entity
public class Flight implements Serializable {
...
@Version
@Column(name="OPTLOCK")
public Integer getVersion() { ... }
}
ここで、バージョンプロパティはOPTLOCK列にマップされ、エンティティマネージャはそれを使用して競合する更新を検出し、last-commit-wins戦略によって上書きされる更新の損失を防ぎます @ version
Grailsの詳細については、以下のリンクを参照してください。Gitハブコードが含まれています。
GRAILS-8937のテスト:HibernateOptimisticLockingFailureException
StaleObjectStateException は、他のプロジェクトで自然に発生する可能性があります。 2つの同時トランザクションが同じエンティティバージョンをロードし、それらのそれぞれがそのエンティティを変更するたびに、楽観的ロック衝突の失敗により、最後に実行されたスレッドが失敗します。
あなたの場合、JPAの境界の外に、この問題を助長する可能性のある追加の呼び出しがあります。
Object o = PObject.lock(profileId)
各トランザクションはスレッドにバインドされており、セッション内で発生するため、2つの競合するスレッドは、同じエンティティIDの2つのオブジェクト参照を保持します。楽観的ロックの目標は、他の明示的なロックメカニズムがなくても効果的に機能します。
変更されたエンティティ参照をsaveObject
バージョンに送信してもこの例外が発生する場合は、2つの競合するスレッドが同じエンティティを変更する可能性が高いことを意味します。
このシナリオでは、 悲観的ロック は、 楽観的フェイルファストアプローチ とは対照的に待機を伴うため、より良い結果が得られます。