REQUIRENEWの使用を避ける[Spring JPAコード生活白書]

8071 ワード

緒論


現在SpringJpaプロジェクトを行っていますが、問題を減らすために個人JPA会議を開きたいと思います.
REQUIRENEWの使用時に発生する可能性のある問題1
同じ識別子を持つオブジェクトは、異なる永続性コンテキストで変更される(
  • ).
  • 次はコードです
        @Transactional
        public String test(){
    
            Member testMember1 = repository.findByMemberByMemberId(2L);
            handler.innerTest();
            testMember1.changeAge(35);
            return null;
    
        }
    test()メソッドはコントローラから呼び出され、innerTestというメソッドが呼び出されます.
        @Transactional(propagation = Propagation.REQUIRES_NEW)
        public void innerTest(){
    
            Member testMember2 = memberRepository.findByMemberByMemberId(2L);
            testMember2.changeName("Patrick");
    
        }
    現在のコード実行前のデータベースのデータは次のとおりです.

    2番のメンバーの名前はテスト年齢33です
    データベースにはマリア・ディビーが使用されています.独立性レベル2 REPEATABLE READ 適用されます.

    結果を予測する

  • REQUIRENEWを使用し、名前はPatrick、年齢は35に変更されます.
  • 原因を知らないで、間違いで、
  • の名前を変えず、35歳に変更した.
  • 結果は3日です.
    実際,2つの方法しかなければ,上記のコードに現れる問題をすぐに見つけることができるが,原理を知っていても
    上図に示すように、コードが作成され、ターゲットが4分の5に入った場合は、どこで変更されたか、変更されていないかを一つ一つ探さなければなりません.
    上記コード実行結果は、REQUIRENEWに適用される1つ以上のコードを反映しない.
    原因を見てみましょう.
    説明の便宜上、test()で開いているオブジェクトをT 1 innerTest()で開いているオブジェクトをT 2と呼ぶ
    T 1で開くオブジェクト
  • testMember 1オブジェクトをインポートすると、上記の永続性コンテキストのメインキャッシュにおいて、ID 2のエンティティがキャッシュされます.
  • innerTestメソッドにtestMember 2をインポートすると、新しい持続性コンテキストが開き、次のように異なる持続性コンテナに入ります.
  • T 2コンテナ変更メンバーのオブジェクト
  • 以降、T 2オブジェクトが終了すると、T 2オブジェクトに対応する恒久コンテナが終了する.
    変更したオブジェクトをリフレッシュした後、SQL文をデバイスに送信してコミットします.
  • が再びT 1に戻ると、T 1内のオブジェクトの年齢は35に変更されます.
  • 以降、id->2、nameは試験年齢35の対象となる
    REQUIRENEWオブジェクトコンテナの変更値はDeviに一度コミットされたが,最終T 1のコンテナオブジェクトに上書きされた.

  • 実はこのような状況に2つの方法があれば、本当に愚かな間違いを犯して、私がなぜこのようにするのかと思ったとき、コードが高度化して、対象が絡み合って、このようなことはいつでも起こり得る.
    REQUIRENEWの使用時に発生する可能性のある問題2
    1つ目の話題でご覧の通り、REQUIRENEWで使用しているメンバーオブジェクトに戻れば良いのではないでしょうか!
    結論->この方法を使うな
    今の状態はこうです.
        @Transactional
        public String test(){
    
            Member testMember1 = handler.innerTest();
            testMember1.changeAge(35);
            return null;
    
        }
    上記のようにtest()メソッドはinnerTest()メソッドを呼び出します.
        @Transactional(propagation = Propagation.REQUIRES_NEW)
        public Member innerTest(){
    
            Member testMember2 = memberRepository.findByMemberByMemberId(2L);
            testMember2.changeName("Patrick");
            return testMember2;
    
        }
    上記のコードの終了時に結果値を予測できます.
    1.REQUITES NEWが実行され、testで名前がPatrick、年齢が35に変更されました.
    [原句]何なのか分かりませんが、間違えました.
    3.年齢は35歳になる.
    4.名前だけが変わる.
    正解は4番
    これは、test()T 1オブジェクトが開始されると、T 1コンテナが第2のメンバーを永続コンテキストに格納しないためです.
    TT 1コンテナには、永続的なコンテキスト内の2番目のメンバーはありません.T 2の2番目のメンバーを保存し、デバイスにリフレッシュし、メンバーをT 1オブジェクトコンテナに戻すことで、T 1持続性コンテキストに入ることができます.大学ではありません.
  • コードの問題は、コードが長ければ長いほど、返される非永続的testMember 1が長くなることである.
    (もちろん、このようなエラーはその前に障害をもたらします)testMember 1->非永続オブジェクトは他の開発者に楽しく遊ばれます.しかし、テレビでも何も起こらない.
  • 改善方法

  • の一番上に目標が掲げられていて、下から二重三重に乗って入るコードを避けています.
    変更を検出してデータベースにアクセスするには、最初にロードする場所、変更を変更する場所が非常に困難になります.
  • エンティティを大幅に変更した場合は、DTOオブジェクト
  • を使用します.
  • エンティティの変更がdiviの変更と同じであることを警戒し、コードを記述します.
  • n/a.結論


    ネットワーク名を使用する場合は、同じIDが異なる永続コンテナで異なるアドレス値に変更されることを避ける必要があります.