Spring @Transactional-分離、伝播


447

誰かが実際の例を使用して、アノテーションの分離伝播のパラメータについて説明できますか@Transactional

基本的に、いつ、なぜ私はデフォルト値を変更することを選択する必要がありますか。

回答:


442

良い質問ですが、答えるのは簡単ではありません。

伝搬

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

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

隔離

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

  • 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複数の実行が同じトランザクションに参加する可能性があるため、aの結果が異なる場合があることにも注意してください。


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

@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()してないで、それはそれ自身のサブトランザクションを作成したので、背中を丸め。

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


最後のリンクは、あなたが話していることとどのように関連していますか?リンクされたドキュメントによると、現在のトランザクションが何であるかを示すのはセッションファクトリではなく、セッションです。
ドナルフェロー

@ドナル、わかりませんでした。私のポイントは、sessionFactory.getCurrentTransaction()追加されたので、HibernateTemplateトランザクションを管理するためにもう実行する必要がないということでした。私はそれを削除:)
ヨハン・シェーベルイ

私の質問は、リンクがどこを指しているのかということでした。:-)
ドナルフェロー

現在、取引に加えられた変更を取得する方法stackoverflow.com/questions/36132667/...
プラザンナクマールHA

304

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

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

PROPAGATION_REQUIRES_NEW = 3 ; メソッドM1に対してDataSourceTransactionObject T1がすでに開始されており、それが進行中の場合(メソッドM1を実行中)。別のメソッドM2の実行が開始されると、T1はメソッドM2の実行中に一時停止され、M2の新しいDataSourceTransactionObject T2で停止します。M2は独自のトランザクションコンテキスト内で実行されます。

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

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

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

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

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

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

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

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

シナリオ2は非反復可能読み取りと呼ばれます。

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

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

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

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

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

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();
    }

}

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


現在、取引に加えられた変更を取得する方法stackoverflow.com/questions/36132667/...
プラザンナクマールHAを

2
分離レベル伝播の間の相互作用は何ですか?メソッド1が分離レベル、たとえばREAD_COMMITTEDでトランザクションを開始し、後でレベル2をREPEATABLE_READで呼び出す場合、メソッド2は、それが指定する伝播動作に関係なく、独自の新しいトランザクションで実行する必要があります(たとえば、必須のみ)?
Cornel Masson

これは本当に番組に遅れますが、PROPAGATION_REQUIRES_NEWのときに、M1に別の新しい呼び出しが発生した場合、T1(M1によって使用されます)はどうなりますか?(たとえばM1.1)
Tim

115

各パラメータについての十分な説明は、他の回答によって与えられます。ただし、実際の例を求めたので、さまざまな伝播オプションの目的を明確にする例を次に示します。

サインアップサービス の実装を担当しているとします。ユーザーに確認メールが送信しているとします。2つのサービスオブジェクトが考えられます。1つはユーザーの登録用、もう1つは電子メールの送信用で、後者は最初のサービスオブジェクト内で呼び出されます。たとえば、次のようなもの:

/* Sign Up service */
@Service
@Transactional(Propagation=REQUIRED)
class SignUpService{
 ...
 void SignUp(User user){
    ...
    emailService.sendMail(User);
 }
}

/* E-Mail Service */
@Service
@Transactional(Propagation=REQUIRES_NEW)
class EmailService{
 ...
 void sendMail(User user){
  try{
     ... // Trying to send the e-mail
  }catch( Exception)
 }
}

2番目のサービスの伝播タイプがREQUIRES_NEWであり、さらに例外がスローされる可能性があることに気づいたかもしれません(SMTPサーバーがダウンしている、無効な電子メールまたはその他の理由)。プロセス全体をロールバックしたくない場合があります。データベースまたはその他のものからユーザー情報を削除する。したがって、別のトランザクションで2番目のサービスを呼び出します。

例に戻って、今度はデータベースのセキュリティに関心があるので、DAOクラスを次のように定義します。

/* User DAO */
@Transactional(Propagation=MANDATORY)
class UserDAO{
 // some CRUD methods
}

つまり、DAOオブジェクト、つまりdbへの潜在的なアクセスが作成されるときは常に、呼び出しがサービスの1つの内部から行われたことを確認し、ライブトランザクションが存在する必要があることを意味します。それ以外の場合は例外が発生します。したがって、伝播のタイプはMANDATORYです。


26
REQUIRES_NEWの完璧な例。
Ravi Thapliyal 2013

5
いい説明だ!ところで、伝播のデフォルトは何ですか?また、このような例を分離のために提供することもできます。どうもありがとう。
Prakash K

5
@PrakashKデフォルトは必須です。(docs.spring.io/spring-framework/docs/current/javadoc-api/org/...
ihebiheb

59

分離レベルは、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動作はトランザクションのスコープ内で実行されます。すでに開いているトランザクションがない場合、メソッドはとにかく実行されますが、非トランザクションの方法で実行されます。


4
どちらを使用するかを追加できれば、はるかに有益です。
Kumar Manish 2016年

例を挙げてください。初心者には非常に役立ちます
nitinsridar

23

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

スプリングにおける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(原子性、整合性、分離、耐久性)プロパティの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(トランザクション)からコミットされていないデータを読み取ります。
  • 繰り返し不可の読み取り:コミットされた読み取りUPDATES別のtxかられた。
  • ファントム読み取り:コミットされた読み取りINSERTSおよび/またはDELETES別のTXからの読み取り

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

+---------------------------+----------------+----------------------+----------------+
| 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       |
+---------------------------+----------------+----------------------+----------------+

例えば


20

Read Uncommitedそれは本当にACID準拠していないので、あなたはほとんど使いたくないでしょう。Read Commmitedデフォルトの出発点として最適です。Repeatable Readおそらく、レポート、ロールアップ、または集約のシナリオでのみ必要です。postgresに含まれている多くのDBは実際には反復可能読み取りをサポートしていないことに注意してくださいSerializable。代わりに使用する必要があります。Serializable他のものから完全に独立して発生しなければならないことがわかっている場合に役立ちます。synchronizedJavaのように考えてください。シリアライズ可能はREQUIRES_NEW伝播と連動します。

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


13

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

トランザクションの分離

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

トランザクションの伝播

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

参照:


1
素晴らしい、すべての情報が1か所にあります。リンクは非常に役立ちます。ありがとう@Gladwin Burboz
nitinsridar

7

私が実行している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の両方に使用されるMain Outerの既存のトランザクション
      • outerMethod-トランザクションあり
      • method_1-Propagation.MANDATORY)-
      • method_2-トランザクションアノテーションのみで、例外をスローします
      • 出力:データベースに永続的なレコードがないことは、ロールバックが行われたことを意味します。
      • outerMethod-トランザクションあり
      • method_1-Propagation.REQUIRES_NEW)
      • method_2-Propagation.REQUIRES_NEW)および1/0例外をスローします
      • 出力:method_2は例外をスローするため、method_2レコードは永続化されません。
      • 出力:method_1はデータベースにレコードを保持します。
      • 出力:method_1のロールバックはありません

3

これを追加できます:

@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
    }
}

1

次のように使用できます:

@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();
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.