1. ホーム
  2. java

[解決済み] Spring @Transactional - 分離、伝搬

2022-03-19 16:50:31

質問

を説明できる人がいますか? アイソレーション &です。 伝搬 パラメータは @Transactional アノテーションの実例を介して?

基本的に、いつ、なぜ、そのデフォルト値を変更することを選択すべきなのでしょうか。

どのように解決するのですか?

良い質問ですね、でも答えるのは簡単なことではありません。

伝搬

トランザクションが互いにどのように関連しているかを定義する。一般的なオプション。

  • REQUIRED : コードは常にトランザクションの中で実行されます。新しいトランザクションを作成するか、利用可能であれば再利用します。
  • REQUIRES_NEW : コードは常に新しいトランザクションで実行されます。現在のトランザクションが存在する場合、それを一時停止します。

のデフォルト値は @TransactionalREQUIRED そして、これはしばしばあなたが望むことです。

分離

トランザクション間のデータコントラクトを定義する。

  • ISOLATION_READ_UNCOMMITTED : ダーティリードを許可します。
  • ISOLATION_READ_COMMITTED : ダーティリードを許可しない。
  • ISOLATION_REPEATABLE_READ : 同じトランザクションで行を2回読み込んでも、結果は常に同じになります。
  • ISOLATION_SERIALIZABLE : すべてのトランザクションを順番に実行します。

異なるレベルは、マルチスレッド・アプリケーションにおいて異なるパフォーマンス特性を持ちます。を理解すれば、そのようなことはないと思います。 ダーティリード の概念を理解すれば、良い選択肢を選ぶことができるはずです。

デフォルトはデータベースの違いにより異なる場合があります。例として マリアDB である。 REPEATABLE READ .


ダーティリードが発生する場合の例。

  thread 1   thread 2      
      |         |
    write(x)    |
      |         |
      |        read(x)
      |         |
    rollback    |
      v         v 
           value (x) is now dirty (incorrect)

したがって、まともなデフォルト(と主張できる場合)は、次のようになります。 ISOLATION_READ_COMMITTED これは、他の実行中のトランザクションによって既にコミットされた値のみを読み取ることができるもので、伝搬レベルとして REQUIRED . そして、アプリケーションに他のニーズがある場合は、そこから作業することができます。


を入力すると、常に新しいトランザクションが作成される実例です。 provideService ルーチンを終了し、退出するときに完了します。

public class FooService {
    private Repository repo1;
    private Repository repo2;

    @Transactional(propagation=Propagation.REQUIRES_NEW)
    public void provideService() {
        repo1.retrieveFoo();
        repo2.retrieveFoo();
    }
}

もし代わりに REQUIRED の場合、トランザクションは は開いたままです。 ルーチンに入るときにすでにトランザクションが開いていた場合。 また、ルーチンに入った時に既にトランザクションが開いていた場合、そのトランザクションの結果は rollback は、複数の実行が同じトランザクションに参加する可能性があるため、異なる可能性があります。


テストで動作を確認し、伝搬レベルによって結果がどのように異なるかを簡単に確認することができます。

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath:/fooService.xml")
public class FooServiceTests {

    private @Autowired TransactionManager transactionManager;
    private @Autowired FooService fooService;

    @Test
    public void testProvideService() {
        TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
        fooService.provideService();
        transactionManager.rollback(status);
        // assert repository values are unchanged ... 
}

プロパゲーションレベルが

  • REQUIRES_NEW を期待します。 fooService.provideService() でした NOT は、独自のサブトランザクションを作成したため、ロールバックされました。

  • REQUIRED : すべてがロールバックされ、バッキングストアは変更されないと予想されます。