web-dev-qa-db-ja.com

Spring @Transactional - 分離、伝播

誰かが isolation / propagation パラメータが@Transactionalアノテーションに何のためにあるのかを実世界の例で説明できますか?

基本的にいつ、なぜ私はそれらのデフォルト値を変更することを選択すべきです。

361
Mat B.

良い質問ですが、簡単な質問ではありません。

Propagation

トランザクションの相互関係を定義します。一般的なオプション:

  • Required:コードは常にトランザクションで実行されます。新しいトランザクションを作成するか、使用可能な場合は再利用します。
  • Requires_new:コードは常に新しいトランザクションで実行されます。現在のトランザクションが存在する場合、中断します。

Isolation

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

  • Read Uncommitted:ダーティリードを許可します。
  • Read Committed:ダーティリードを許可しません。
  • Repeatable Read:同じトランザクションで行が2回読み取られた場合、結果は常に同じになります。
  • Serializable:シーケンス内のすべてのトランザクションを実行します。

マルチスレッドアプリケーションでは、レベルが異なるとパフォーマンス特性も異なります。 dirty readsの概念を理解すれば、適切なオプションを選択できると思います。


ダーティリードが発生する可能性のある例:

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

したがって、健全なデフォルト(要求できる場合)は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 newfooService.provideService()ではなくがロールバックされ、それが独自のサブトランザクションを作成したため、ロールバックされます。

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

390
Johan Sjöberg

PROPAGATION_REQUIRED =; DataSourceTransactionObject T1がメソッドM1で既に開始されている場合。別のメソッドM2トランザクションオブジェクトが必要な場合、新しいトランザクションオブジェクトは作成されません。同じオブジェクトT1がM2に使用されます

PROPAGATION_MANDATORY = 2;メソッドはトランザクション内で実行する必要があります。進行中の既存のトランザクションがない場合、例外がスローされます

PROPAGATION_REQUIRES_NEW =;メソッドM1のDataSourceTransactionObject T1が既に開始されており、進行中の場合(メソッドM1を実行中)、別のメソッドM2の実行が開始されると、独自のトランザクションコンテキスト内で実行されるM2.M2の新しいDataSourceTransactionObject T2を使用して、メソッドM2の期間T1が中断されます

PROPAGATION_NOT_SUPPORTED = 4; DataSourceTransactionObject T1がメソッドM1に対してすでに開始されている場合。別のメソッドM2が同時に実行されている場合。M2はトランザクションコンテキスト内で実行しないでください。 T1はM2が完了するまで中断されます。

PROPAGATION_NEVER = 5;トランザクションコンテキストで実行されるメソッドはありません。

分離レベル:トランザクションが他の同時トランザクションのアクティビティによってどの程度影響を受ける可能性があるか。多くのテーブルのデータを一貫性のある状態に保つ一貫性をサポートします。データベースの行やテーブルをロックする必要があります。

複数のトランザクションの問題

シナリオ1。T1トランザクションが別の並行トランザクションT2によって書き込まれたテーブルA1からデータを読み取る場合、T2がロールバックされる途中で、T1によって取得されたデータは無効です。たとえば、a = 2は元のT1がT2によって書き込まれたa = 1を読み取った場合、T2がロールバックした場合、DB = 1ではa = 1がa = 2にロールバックされますが、T1にはa = 1がありますが、DBテーブルではa =に変更されます2。

Scenario2。T1トランザクションがテーブルA1からデータを読み取る場合、別の同時トランザクション(T2)がテーブルA1のデータを更新する場合、T1が読み取ったデータはテーブルA1とは異なります。T2がデータを更新したためたとえば、T1がa = 1を読み取り、T2がa = 2を更新した場合、テーブルA1。

シナリオ。T1トランザクションが特定の行数でテーブルA1からデータを読み取る場合。別の同時トランザクション(T2)がテーブルA1にさらに行を挿入する場合。T1によって読み取られる行の数は、テーブルA1の行とは異なります。

シナリオ1はダーティリードと呼ばれます

シナリオ2は繰り返し不可の読み取りと呼ばれます

シナリオ3は、ファントム読み取りと呼ばれます

したがって、分離レベルは、シナリオ1、シナリオ2、シナリオを防止できる範囲です。ロックを実装することにより、完全な分離レベルを取得できます。これにより、同じデータへの同時読み取りおよび書き込みが発生しなくなりますが、パフォーマンスに影響します。分離レベルは、アプリケーションごとに必要な分離レベルによって異なります。

ISOLATION_READ_UNCOMMITTED:まだコミットされていない変更を読み取ることができます。シナリオ1、シナリオ2、シナリオ3の影響を受けます。

ISOLATION_READ_COMMITTED:コミットされた並行トランザクションからの読み取りを許可します。他のトランザクションがデータを更新している可能性があるため、シナリオ2およびシナリオ3の影響を受ける可能性があります。

ISOLATION_REPEATABLE_READ:同じフィールドの複数の読み取りは、それ自体で変更されるまで同じ結果をもたらします。他のトランザクションがデータを挿入している可能性があるため

ISOLATION_SERIALIZABLE:シナリオ1、シナリオ2、シナリオ3は決して発生しません。完全に分離されており、完全なロックを伴います。ロックのためにパフォーマンスに影響を与えます。

を使用してテストできます

public class TransactionBehaviour {
   // set is either using xml Or annotation
    DataSourceTransactionManager manager=new DataSourceTransactionManager();
    SimpleTransactionStatus status=new SimpleTransactionStatus();
   ;


    public void beginTransaction()
    {
        DefaultTransactionDefinition Def = new DefaultTransactionDefinition();
        // overwrite default PROPAGATION_REQUIRED and ISOLATION_DEFAULT
        // set is either using xml Or annotation
        manager.setPropagationBehavior(XX);
        manager.setIsolationLevelName(XX);

        status = manager.getTransaction(Def);

    }

    public void commitTransaction()
    {


            if(status.isCompleted()){
                manager.commit(status);
        } 
    }

    public void rollbackTransaction()
    {

            if(!status.isCompleted()){
                manager.rollback(status);
        }
    }
    Main method{
        beginTransaction()
        M1();
        If error(){
            rollbackTransaction()
        }
         commitTransaction();
    }

}

分離と伝播のさまざまな値を使用して、デバッグして結果を確認できます。

259

分離レベル 1つのトランザクションによってあるデータリポジトリに加えられた変更が他の同時並行トランザクションにどのように影響するか、またその変更されたデータが他のトランザクションで利用可能になる方法と時期を定義します。 Springフレームワークを使ってトランザクションを定義するとき、同じトランザクションがどの独立性レベルで実行されるかを設定することもできます。

@Transactional(isolation=Isolation.READ_COMMITTED)
public void someTransactionalMethod(Object obj) {

}

READ_UNCOMMITTED分離レベルは、トランザクションが他のトランザクションによってまだコミットされていないデータを読み取る可能性があることを示します。

READ_COMMITTED分離レベルは、他のトランザクションによってまだコミットされていないデータをトランザクションが読み取ることができないことを示します。

REPEATABLE_READ分離レベルは、トランザクションがデータベースから1つのレコードを複数回読み取る場合、それらすべての読み取り操作の結果は常に同じでなければならないことを示します。

SERIALIZABLE分離レベルは、すべての分離レベルの中で最も制限が厳しいです。トランザクションはすべてのレベル(読み取り、範囲、書き込みロック)でロックされて実行されるため、それらは直列化された方法で実行されたように見えます。

伝播 は、ビジネスメソッドを論理トランザクションまたは物理トランザクションの両方にカプセル化する方法を決定する機能です。

Spring REQUIREDの振る舞いは、現在のbeanメソッド実行コンテキストに既に開かれているトランザクションがある場合、同じトランザクションが使用されることを意味します。

REQUIRES_NEWの動作は、新しい物理トランザクションが常にコンテナによって作成されることを意味します。

NESTED動作により、ネストされたSpringトランザクションは同じ物理トランザクションを使用しますが、ネストされた呼び出しの間にセーブポイントを設定するので、内部トランザクションも外部トランザクションとは無関係にロールバックできます。

MANDATORYの振る舞いは、既存のオープンされたトランザクションが既に存在していなければならないと述べています。そうでない場合は、コンテナによって例外がスローされます。

NEVERの動作は、既存の開かれたトランザクションがまだ存在してはならないと述べています。トランザクションが存在する場合は、コンテナによって例外がスローされます。

NOT_SUPPORTED動作はトランザクションの範囲外で実行されます。開いているトランザクションがすでに存在する場合は一時停止します。

開かれたトランザクションがすでに存在する場合、SUPPORTS動作はトランザクションの範囲内で実行されます。まだ開かれていないトランザクションがない場合、メソッドはとにかく非トランザクション的な方法で実行されます。

45
reos

ACIDに準拠していないため、Read Uncommitedを使用することはほとんどありません。 Read Commmitedはデフォルトの良い出発点です。 Repeatable Readは、レポート、ロールアップ、または集計のシナリオでのみおそらく必要です。 postgresを含む多くのDBが実際にRepeatable Readをサポートしていないことに注意してください、代わりにSerializableを使用しなければなりません。 Serializableは、他のこととは完全に独立して行わなければならないとわかっていることに役立ちます。 Javaではsynchronizedのように考えてください。シリアライズ可能はREQUIRES_NEWの伝播と密接に関係しています。

UPDATEまたはDELETEクエリを実行するすべての関数と、 "サービス"レベルの関数にはREQUIRESを使用します。 SELECTのみを実行するDAOレベルの関数では、TXが既に開始されている(つまりサービス関数から呼び出されている)場合はTXに参加するSUPPORTSを使います。

19
AngerClown

トランザクションの分離とトランザクションの伝播は関連していますが、明らかに2つの非常に異なる概念です。どちらの場合も、デフォルトは 宣言的トランザクション管理 または プログラムによるトランザクション管理 のいずれかを使用して、クライアント境界コンポーネントでカスタマイズされます。各分離レベルと伝搬属性の詳細は、以下の参照リンクにあります。

トランザクション分離

データベースへの2つ以上の実行中のトランザクション/接続について、1つのトランザクション内のクエリによって行われた変更は、いつどのように変更され、別のトランザクション内のクエリにも反映されます。また、このトランザクションの変更を他のトランザクションから分離するためにどのようなデータベースレコードロックを使用するか、またその逆の関係もあります。これは通常、トランザクションに参加しているデータベース/リソースによって実装されます。

トランザクション伝播

任意の特定の要求/処理のためのエンタープライズアプリケーションでは、仕事を終わらせるために関わる多くのコンポーネントがあります。このコンポーネントのいくつかは、それぞれのコンポーネントとそのサブコンポーネントで使用されるトランザクションの境界(開始/終了)をマークします。このコンポーネントのトランザクション境界に対して、Transaction Propogationは、各コンポーネントがトランザクションに参加するかどうか、および呼び出し側コンポーネントがすでにトランザクションを作成または開始しているかどうかにかかわらずどうなるかを指定します。これは、Java EEのトランザクション属性と同じです。これは通常、クライアントのトランザクション/接続マネージャによって実装されます。

参照:

12
Gladwin Burboz

トランザクションは、データベースとの作業単位を表します。

Spring準拠のトランザクションプロパティを定義するTransactionDefinitionインターフェース。 @Transactionalアノテーションはメソッドまたはクラスのトランザクション属性を記述します。

@Autowired
private TestDAO testDAO;

@Transactional(propagation=TransactionDefinition.PROPAGATION_REQUIRED,isolation=TransactionDefinition.ISOLATION_READ_UNCOMMITTED)
public void someTransactionalMethod(User user) {

  // Interact with testDAO

}

伝播(複製): はトランザクション間の関係に使用されます。 (Javaスレッド間通信に類似)

+-------+---------------------------+------------------------------------------------------------------------------------------------------+
| value |        Propagation        |                                             Description                                              |
+-------+---------------------------+------------------------------------------------------------------------------------------------------+
|    -1 | TIMEOUT_DEFAULT           | Use the default timeout of the underlying transaction system, or none if timeouts are not supported. |
|     0 | PROPAGATION_REQUIRED      | Support a current transaction; create a new one if none exists.                                      |
|     1 | PROPAGATION_SUPPORTS      | Support a current transaction; execute non-transactionally if none exists.                           |
|     2 | PROPAGATION_MANDATORY     | Support a current transaction; throw an exception if no current transaction exists.                  |
|     3 | PROPAGATION_REQUIRES_NEW  | Create a new transaction, suspending the current transaction if one exists.                          |
|     4 | PROPAGATION_NOT_SUPPORTED | Do not support a current transaction; rather always execute non-transactionally.                     |
|     5 | PROPAGATION_NEVER         | Do not support a current transaction; throw an exception if a current transaction exists.            |
|     6 | PROPAGATION_NESTED        | Execute within a nested transaction if a current transaction exists.                                 |
+-------+---------------------------+------------------------------------------------------------------------------------------------------+

分離: 分離は、データベーストランザクションのACID(Atomicity、Consistency、Isolation、Durability)プロパティの1つです。分離は、トランザクションの整合性が他のユーザーやシステムにどのように見えるかを決定します。リソースのロック、つまり同時実行制御に使用します。ある時点で1つのトランザクションだけがリソースにアクセスできるようにします。

ロックの認識: 分離レベルはロックが保持される期間を決定します。

+---------------------------+-------------------+-------------+-------------+------------------------+
| Isolation Level Mode      |  Read             |   Insert    |   Update    |       Lock Scope       |
+---------------------------+-------------------+-------------+-------------+------------------------+
| READ_UNCOMMITTED          |  uncommitted data | Allowed     | Allowed     | No Lock                |
| READ_COMMITTED (Default)  |   committed data  | Allowed     | Allowed     | Lock on Committed data |
| REPEATABLE_READ           |   committed data  | Allowed     | Not Allowed | Lock on block of table |
| SERIALIZABLE              |   committed data  | Not Allowed | Not Allowed | Lock on full table     |
+---------------------------+-------------------+-------------+-------------+------------------------+

読み取りの認識: 次の3種類の大きな問題が発生します。

  • ダーティーリード:他のtxからコミットされていないデータを読み込みます(トランザクション)。
  • 繰り返し不可の読み取り:他のTXからコミット済みのUPDATESを読み取ります。
  • ファントム読み取り:他のtxからコミットされたINSERTSまたはDELETESを読み取ります。

読み取りの種類が異なる分離レベル

+---------------------------+----------------+----------------------+----------------+
| Isolation Level Mode      |  Dirty reads   | Non-repeatable reads | Phantoms reads |
+---------------------------+----------------+----------------------+----------------+
| READ_UNCOMMITTED          | allows         | allows               | allows         |
| READ_COMMITTED (Default)  | prevents       | allows               | allows         |
| REPEATABLE_READ           | prevents       | prevents             | allows         |
| SERIALIZABLE              | prevents       | prevents             | prevents       |
+---------------------------+----------------+----------------------+----------------+

たとえば

8
Premraj

outerMethodmethod_1method_2を異なる伝播モードで実行しました。

下記は異なる伝搬モードの出力です。

  • 外法

    @Transactional
    @Override
    public void outerMethod() {
        customerProfileDAO.method_1();
        iWorkflowDetailDao.method_2();
    }
    
  • Method_1

    @Transactional(propagation=Propagation.MANDATORY)
    public void method_1() {
        Session session = null;
        try {
            session = getSession();
            Temp entity = new Temp(0l, "XXX");
            session.save(entity);
            System.out.println("Method - 1 Id "+entity.getId());
        } finally {
            if (session != null && session.isOpen()) {
            }
        }
    }
    
  • 方法2

    @Transactional()
    @Override
    public void method_2() {
        Session session = null;
        try {
            session = getSession();
            Temp entity = new Temp(0l, "CCC");
            session.save(entity);
            int i = 1/0;
            System.out.println("Method - 2 Id "+entity.getId());
        } finally {
            if (session != null && session.isOpen()) {
            }
        }
    }
    
      • outerMethod - トランザクションなし
      • method_1 - Propagation.MANDATORY) -
      • method_2 - トランザクションアノテーションのみ
      • 出力:method_1は、既存のトランザクションがないという例外をスローします
      • outerMethod - トランザクションなし
      • method_1 - トランザクションアノテーションのみ
      • method_2 - Propagation.MANDATORY)
      • 出力:method_2は、既存のトランザクションがないという例外をスローします
      • 出力:method_1はデータベース内のレコードを永続化します。
      • outerMethod - トランザクションあり
      • method_1 - トランザクションアノテーションのみ
      • method_2 - Propagation.MANDATORY)
      • 出力:method_2はデータベース内のレコードを永続化します。
      • 出力:method_1はデータベース内のレコードを永続化します。 - ここでは方法1と2の両方に使用されるメイン外部既存トランザクション
      • outerMethod - トランザクションあり
      • method_1 - Propagation.MANDATORY) -
      • method_2 - トランザクションの注釈のみで、例外をスローします
      • 出力:データベースにレコードが残っていない場合は、ロールバックが行われたことを意味します。
      • outerMethod - トランザクションあり
      • method_1 - 伝播.REQUIRES_NEW)
      • method_2 - Propagation.REQUIRES_NEW)そして1/0例外をスローします
      • 出力:method_2は例外をスローし、method_2レコードは永続化されません。
      • 出力:method_1はデータベース内のレコードを永続化します。
      • 出力:method_1のロールバックはありません
7
NIrav Modi

これを追加することができます。

@Transactional(readOnly = true)
public class Banking_CustomerService implements CustomerService {

    public Customer getDetail(String customername) {
        // do something
    }

    // these settings have precedence for this method
    @Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
    public void updateCustomer(Customer customer) {
        // do something
    }
}
3
Ankit

あなたはこのように使うことができます:

@Transactional(propagation = Propagation.REQUIRES_NEW)
public EventMessage<ModificaOperativitaRapporto> activate(EventMessage<ModificaOperativitaRapporto> eventMessage) {
//here some transaction related code
}

あなたもこの事を使うことができます:

public interface TransactionStatus extends SavepointManager {
    boolean isNewTransaction();
    boolean hasSavepoint();
    void setRollbackOnly();
    boolean isRollbackOnly();
    void flush();
    boolean isCompleted();
}
1
Ankit