あなたが説明した問題はデータベースによって処理されず、私の経験から、Hibernate でも完全に処理されるわけではありません。
問題にならないように、明確な手順を踏む必要があります。
Hibernate がいくつかの作業を行います。前の回答によると、Hibernate は、分離されたフラッシュ内で、挿入、削除、および更新が達成可能な順序で適用されるように順序付けされることを保証します。AbstractFlushingEventListener クラスのperformExecutions(EventSource セッション)を参照してください。
外部キー制約に違反しないように、すべての SQL (および二次キャッシュの更新) を特別な順序で実行します。
- 挿入、実行された順序で
- アップデート
- コレクション要素の削除
- コレクション要素の挿入
- 実行された順序で削除します
一意の制約がある場合、この順序を知っておくことは非常に重要です。特に、1 対多の子を置き換えたい場合 (古いものを削除/新しいものを挿入)、古い子と新しい子の両方が同じ一意の制約 (たとえば、同じ電子メール アドレス) を共有する場合は特に重要です。 )。この場合、削除/挿入する代わりに古いエントリを更新するか、削除後にフラッシュしてから挿入を続けることができます。より詳細な例については、この記事を確認してください。
更新の順序は指定されていないことに注意してください。Hibernate コードを調べると、更新順序はエンティティが更新された順序ではなく、永続化コンテキストに追加された順序に依存すると思います。それはあなたのコードでは予測可能かもしれませんが、Hibernate のコードを読んでも、その順序に頼る気がしませんでした。
私が考えることができる3つの解決策があります:
- hibernate.order_updatesをtrueに設定してみてください。これは、同じテーブル内の複数の行が更新されている場合のデッドロックを回避するのに役立ちますが、複数のテーブルにわたるデッドロックには役立ちません。
- 更新を行う前に、トランザクションがエンティティの 1 つでPESSIMISTIC_WRITEロックを取得するようにします。使用するエンティティは特定の状況によって異なりますが、デッドロックのリスクがある場合に一貫してエンティティが選択されることを保証する限り、ロックが取得されるまで残りのトランザクションがブロックされます。
- デッドロックが発生したときにキャッチし、適切な方法で再試行するようにコードを記述します。デッドロックの再試行を管理するコンポーネントは、現在のトランザクション境界の外に配置する必要があります。これは、失敗したセッションを閉じて、関連するトランザクションをロールバックする必要があるためです。この記事では、自動再試行 AOP アスペクトの例を見つけることができます。