0

Spring マネージド トランザクションで奇妙な動作が発生しています。これは Spring MVC アプリです。エンティティを Web 層に直接バインドしています。@ModelAttribute でアノテーションが付けられたメソッドから呼び出される次のサービス層コードを使用して、データベースから永続エンティティをロードして編集します。

@Transactional(readOnly = true)
@PreAuthorize("#id == authentication.principal.id or hasRole('ROLE_ADMIN')")
public User findById(Long id) {
    return repository.findOne(id);
}

フォームが送信されると、データはこの切り離されたエンティティにバインドされます。検証の一環として、データベースにクエリを実行して、ユーザーに指定された電子メール アドレスが一意であることを検証します。

@Transactional(readOnly = true)
public boolean isEmailAddressUnique(String emailAddress, Long userId){
    return repository.checkEmailAddressUnique(emailAddress, userId) == 0;
}

この時点で、このクエリを実行する前に、Hibernate は以前にロードされた切り離されたエンティティをフラッシュしようとしますが、これは理解できません。メールアドレスが一意でない場合、明らかに例外が発生します。

私は OpenEntityManagerinViewFilter を使用していますが、これには FlushMode を NEVER に設定する必要があります。

isEmailAddressUnique() から @Transactional 属性を削除すると、フラッシュは発生せず、すべてが期待どおりに機能し、問題が解決すると思われますが、ここで何が起こっているのかを理解したいと思います。

助言がありますか?

=============

わかりました、さらに調査しました。したがって、デフォルトで構成された OpenEntityManagerInViewFilter があります。

シナリオ 1。

[1] MVC コントローラーは、読み取り専用トランザクション T1 でユーザー エンティティをロードします。[2] MVC フィールド バインディングの結果としてエンティティが変更されます。[3] 2 番目のトランザクションは、読み取り専用トランザクション T2 でユーザー テーブルに対して実行されます。[4] Hibernate は、T1 にロードされたエンティティをフラッシュします。[5] Hibernate がクエリを発行する

シナリオ 2。

OEMIVFilter をサブクラス化して、FlushMode を commit に設定しました (デフォルトは auto)。

上記のように、手順 [4] と [5] を逆にします。T2 が readOnly としてマークされていても、Hibernate はトランザクションの最後に Flush を試みます。readOnly は現在のトランザクション T2 のみを参照する可能性がある (そして、T2 を実行する前にデータベースの同期を試みる) ため、シナリオ 1 を理解できました。ただし、シナリオ 2 ではフラッシュは発行されないはずです。

4

1 に答える 1