web-dev-qa-db-ja.com

StaleObjectstateException行が更新または削除されました

Hibernateを使用するSpringFrameworkに基づくWebアプリケーションのコントローラーでこの例外が発生します。私はこれに対抗するために多くの方法を試しましたが、それを解決することができませんでした。

コントローラのメソッドhandleRequestInternalでは、送信アクションでない限り、主に「読み取り」のためにデータベースに対して呼び出されます。 Spring's Sessionを使用していますが、getHibernateTemplate()に移動しましたが、まだ問題が残っています。

基本的に、これはデータベースへの2番目の呼び出しでこの例外がスローされます。あれは:

1)getEquipmentsByNumber(number) {最初に、プロパティのリストがあり、各プロパティに値のリストがある「番号」に基づいて、機器がDBからフェッチされます。これらの値(プリミティブオブジェクトStrings)をループして、変数に読み込みます)

2)getMaterialById(id) {IDに基づいて素材を取得する}

2番目の呼び出しは、おそらく「フラッシュ」するセッションを行っていることを理解していますが、私はオブジェクトを「読み取っている」だけです。何も変更されていないのに、2番目の呼び出しがEquipmentプロパティで古いオブジェクトの状態例外をスローするのはなぜですか。 ?

ビューに渡すオブジェクトでLazyExceptionsが発生するため、呼び出し後にキャッシュをクリアできません。

私はこれを読みました: https://forums.hibernate.org/viewtopic.php?f=1&t=996355&start= 提供された提案に基づいて問題を解決できませんでした。

この問題を解決するにはどうすればよいですか?どんなアイデアや考えも大歓迎です。

UPDATE:私がテストしたばかりのことは、プロパティのリストから変数を読み取った後の関数getEquipmentsByNumber()で、次のことを行います:getHibernateTemplate().flush();そして今では例外はこの行にあり、マテリアルをフェッチするための呼び出しではありません(つまり、getMaterialById(id))。

UPDATE:flushを明示的に呼び出す前に、オブジェクトをセッションキャッシュから削除して、古いオブジェクトがキャッシュに残らないようにします。

getHibernateTemplate().evict(equipment);
getHibernateTemplate().flush();

OK、これを行った後、問題はDBからの次のフェッチに移動しました。メソッドに同期済みのラベルを付け、内容を読み終えたらすぐにオブジェクトを削除する必要があると思います。あまり良く聞こえません。

UPDATE:handleRequestInternalメソッドを「同期」しました。エラーは消えた。もちろん、最善の解決策ではなく、何をすべきか!現在のセッションを閉じて新しいセッションを開くためにhandleRequestInternalを試しました。ただし、アプリの他の部分が正しく機能しなくなる可能性があります。機能しないThreadLocalを使用しようとしました。

16
Saky

Hibernateを何らかの方法で誤用しているため、更新中または削除中データベースからのオブジェクト。

そのため、flush()を呼び出すと例外がスローされます。

1つの可能性:サーブレットまたはコントローラーのメンバーフィールドを介して、セッションまたはエンティティを誤って「共有」しています。これが、「同期」によってエラーの症状が変わる主な理由です。簡単な解決策:これは絶対に行わないでください。セッションとエンティティはこのように機能するべきではなく、機能しないはずです。各リクエストは個別に処理される必要があります。

別の可能性:unsaved-value「int」PKフィールドのデフォルトは0です。本当に有効なPK値として0を使用したい場合は、代わりにこれらを「整数」として入力できる場合があります。

3番目の提案:Hibernateセッションを明示的に使用し、機能する単純な正しいコードを書くことを学ぶ、次にJavaソースをロードして、内容を読んで理解できるようにします)これらのライブラリは実際にあなたのためにやっています。

6
Thomas W

私もこの例外に苦労していましたが、オブジェクトにロックをかけても再発する場合(そして、オブジェクトに触れる唯一のプロセスであることがわかっているテスト環境で)、括弧を付けることにしました。スタックトレースでは、十分な考慮が必要です。

org.hibernate.StaleObjectStateException:別のトランザクションによって行が更新または削除された(または未保存の値のマッピングが正しくなかった):[com.rc.model.mexp.MerchantAccount#59132]

私たちの場合、マッピングが間違っていることがわかりました。我々は持っていた type="text"データベース内のミディアムテキストタイプである1つのフィールドのマッピングで、少なくとも特定の状況下では、Hibernateはそれを本当に嫌っています。このフィールドのマッピングから型指定を完全に削除し、問題は解決されました。

奇妙なことに、本番環境では、問題のあるマッピングが適切に行われているため、この例外が発生しません。これがなぜあるのか誰かが何か考えを持っていますか?私たちは同じバージョンのMySQLを使用しています-"5.0.22-log"( "-log"の意味はわかりません)-開発環境と本番環境で。

3
barclay

ここには3つの可能性があります(正確にはわかりませんが、使用している休止状態セッションの処理の種類は何ですか)次々に追加してテストします。

親オブジェクトと子オブジェクトの間でinverse=trueを使用した双方向マッピングを使用して、親または子の変更が関係のもう一方の端に適切に伝播されるようにします。

Optimistic LockingTimeStampまたはVersion列のサポートを追加

join queryを使用して、オブジェクトグラフ全体(parent + children)をまとめてフェッチし、2番目の呼び出しを完全に回避します。

最後に、何も機能しない場合に限ります:Id(既に持っています)で親を再度ロードし、変更されたデータを入力してから更新します。

人生は良いでしょう! :)

2

この問題は私が経験したことであり、非常に苛立たしいものでしたが、DAO/Hibernateの呼び出しで少し奇妙なことが起こっている必要があります。これは、IDでルックアップを実行している場合、古い状態になる理由がないためです。 、それはオブジェクトの単純なルックアップであるためです。

まず、すべてのメソッドに@Transaction(required=true) // you'll have to look up the exact syntaxの注釈が付けられていることを確認します

ただし、この例外は通常、取得元のセッションから切り離されたオブジェクトを変更しようとするとスローされます。多くの場合、これに対する解決策は単純ではなく、何が起こっているかを正確に確認できるように、より多くのコードをポストする必要があります。私の一般的な提案は、単一のトランザクション内でこれらの種類のことを実行する@Serviceを作成することです。

0
walnutmon