序文
Springは、TransactionDefinitionインターフェイスで7種類のトランザクション伝播動作を指定します。トランザクション伝播動作は、Springフレームワークに固有のトランザクション強化機能であり、トランザクションの実際のプロバイダーのデータベース動作に属していません。これは、Springが私たちに提供する強力なツールボックスであり、トランザクション伝播ラインを使用すると、開発の取り組みに多くの便利さを提供できます。しかし、人々はそれについて多くの誤解を持っています、そしてあなたは「サービス方法ビジネスはネストされないのが最善である」という噂を聞いていたに違いありません。ツールを正しく使用するには、まずツールを理解する必要があります。この記事では、7つのトランザクション伝播行動を詳細に紹介し、コンテンツの主要なコード例を示します。
基本概念
1.トランザクション通信の動作とは何ですか?
トランザクション伝播動作は、特定のトランザクション伝播挙動によって変更された方法が別の方法にネストされている場合に、トランザクションがどのように伝播されるかを説明するために使用されます。
擬似コードを使用して説明します。
public void methoda(){methodb(); // dosomething} @transaction(propagation = xxx)public void methodb(){// dosomething}コードのmethodA()メソッドはネストされたmethodB()メソッドを呼び出し、 methodB()のトランザクション伝播挙動は@Transaction(Propagation=XXX)設定によって決定されます。ここでは、 methodA()がトランザクションを開始しないことに注意してください。特定のトランザクション伝播動作を変更する方法は、トランザクションを開始する周辺の方法で呼び出される必要はありません。
2。春の7つのトランザクション伝播行動
| トランザクション伝播動作タイプ | 説明します |
|---|---|
| propagation_required | 現在トランザクションがない場合は、新しいトランザクションを作成し、すでにトランザクションがある場合は、トランザクションに追加します。これは最も一般的な選択です。 |
| propagation_supports | 現在のトランザクションをサポートし、現在トランザクションがない場合、非貿易の方法で実行されます。 |
| propagation_mandatory | 現在のトランザクションを使用して、現在トランザクションがない場合は例外をスローします。 |
| propagation_requires_new | 新しいトランザクションを作成します。トランザクションが現在存在する場合は、現在のトランザクションを一時停止します。 |
| propagation_not_supported | 非遷移的な方法で操作を実行し、トランザクションが現在存在する場合、現在のトランザクションが停止されます。 |
| propagation_never | トランザクションが現在存在する場合は、非伝統的な方法で実行され、例外をスローします。 |
| propagation_nested | トランザクションが現在存在する場合、ネストされたトランザクション内で実行されます。現在トランザクションがない場合は、Propagation_Requiredに似た操作を実行します。 |
定義は非常にシンプルで理解しやすいです。コードテストセクションにアクセスして、理解が正しいかどうかを確認しましょう。
コード検証
この記事のコードは、従来の3層構造、つまりサービスとDAO層の2つの層で表示されます。 Springは、依存関係の注入と注釈トランザクション管理を担当します。 DAO層はMyBatisによって実装されています。 Hibernate、JPA、JDBCtemplateなどのお気に入りの方法を使用することもできます。データベースはMySQLデータベースを使用します。また、検証結果に影響しないトランザクション対応データベースを使用することもできます。
最初に、データベースに2つのテーブルを作成します。
ユーザー1
テーブル `user1`(` id` integer unsigned not null auto_increment、 `name` varchar(45)not null default ''、primary key(` did`))Engine = innodb;
ユーザー2
テーブル `user2`( `dot null auto_increment、` name'varchar(45)not null default ''、primary key( `did`))Engine = innodb;
次に、対応するBeanおよびDaoレイヤーコードを書きます。
ユーザー1
public class user1 {private integer id;プライベート文字列名; //メソッドを取得して設定します...}ユーザー2
public class user2 {private integer id;プライベート文字列名; //メソッドを取得して設定します...}user1mapper
public interface user1mapper {int insert(user1 record); user1 selectbyprimarykey(integer id); //他の方法は省略されています...}user2mapper
public interface user2mapper {int insert(user2 record); user2 selectbyprimarykey(integer id); //他の方法は省略されています...}最後に、特定の検証コードはサービスレイヤーによって実装され、次の状況でリストします。
1.propagation_required
cropagation.iser1serviceとuser2serviceの対応する方法にPropagation.REQUIRED属性を追加します。
user1Serviceメソッド:
@servicepublic class user1serviceimpl explention user1service {// Omit other ... @override @transactional(propagation = propagation.required)public void addRequired(user1 user){user1mapper.insert(user); }}user2serviceメソッド:
@servicepublic class user2serviceimpl explention user2service {// omit other ... @override @transactional(propagation = propagation.required)public void addRequired(user2 user){user2mapper.insert(user); } @override @TransActional(propagation = propagation.required)public void addRequiredException(user2 user){user2mapper.insert(user);新しいruntimeexception()を投げる; }} 1.1シーン1
このシナリオ周辺法は、トランザクションを有効にしません。
検証方法1:
@Override public void nottransaction_exception_required_required(){user1 user1 = new user1(); user1.setName( "Zhang San"); user1service.addrequired(user1); user2 user2 = new user2(); user2.setname( "li si"); user2service.addrequired(user2);新しいruntimeexception()を投げる; }検証方法2:
@Override public void nottransaction_required_required_exception(){user1 user1 = new user1(); user1.setName( "Zhang San"); user1service.addrequired(user1); user2 user2 = new user2(); user2.setname( "li si"); user2service.addrequiredexception(user2); }検証方法を個別に実行すると、結果:
検証方法の結果の分析シリアル番号データベース
| 検証方法シリアル番号 | データベースの結果 | 結果分析 |
|---|---|---|
| 1 | 「Zhang San」と「Li Si」が両方とも挿入されています。 | 周辺法はトランザクションを開始しておらず、「Zhang San」および「Li Si」メソッドの挿入は、独自のトランザクションで独立して実行されます。異常な周辺法は、「Zhang San」および「Li Si」メソッドの内部挿入に影響しません。 |
| 2 | 「Zhang San」が挿入されますが、「Li Si」は挿入されていません。 | 周辺法にはトランザクションがなく、「Zhang San」と「Li Si」を挿入する方法は両方とも独自のトランザクションで独立して実行されるため、「Li Si」メソッドの挿入は「Li Si」メソッドのみをロールバックし、「Zhang San」メソッドを挿入することは影響を受けません。 |
結論:これらの2つの方法を通して、伝播によって変更された内部方法が、周辺法がトランザクションを開かず、オープンしたトランザクションが互いに独立しており、互いに干渉しない場合、独自のトランザクションを新たに開くことを証明します。
1.2シーン2
周辺法はトランザクションを開始します。これは、使用率が比較的高いシナリオです。
検証方法1:
@Override @TransActional(Propagation = opagation.required)public void transaction_exception_required_required(){user1 user1 = new user1(); user1.setName( "Zhang San"); user1service.addrequired(user1); user2 user2 = new user2(); user2.setname( "li si"); user2service.addrequired(user2);新しいruntimeexception()を投げる; }検証方法2:
@Override @TransActional(Propagation = opagation.required)public void transaction_required_required_exception(){user1 user1 = new user1(); user1.setName( "Zhang San"); user1service.addrequired(user1); user2 user2 = new user2(); user2.setname( "li si"); user2service.addrequiredexception(user2); }検証方法3:
@transactional @Override public void transaction_required_required_exception_try(){user1 user1 = new user1(); user1.setName( "Zhang San"); user1service.addrequired(user1); user2 user2 = new user2(); user2.setname( "li si"); try {user2service.addrequiredexception(user2); } catch(Exception e){System.out.println( "Method Rollback"); }}検証方法を個別に実行すると、結果:
| 検証方法シリアル番号 | データベースの結果 | 結果分析 |
|---|---|---|
| 1 | 「Zhang San」と「Li Si」は挿入されていません。 | 周辺法は、トランザクションを開始し、内部メソッドは周辺メソッドトランザクションに結合し、周辺メソッドがロールバックされ、内部メソッドもロールバックする必要があります。 |
| 2 | 「Zhang San」と「Li Si」は挿入されていません。 | 周辺法はトランザクションを開き、内部メソッドは周辺メソッドトランザクションを追加し、内部メソッドは例外ロールバックをスローし、周辺メソッドは例外を認識し、全体的なトランザクションがロールバックを引き起こします。 |
| 3 | 「Zhang San」と「Li Si」は挿入されていません。 | 周辺法により、トランザクションが開き、内部メソッドは周辺メソッドトランザクションに結合され、内部メソッドは例外ロールバックをスローします。方法がキャッチされ、周辺法によって認識されていない場合でも、トランザクション全体が引き戻されます。 |
結論:上記の実験結果は、周辺法がトランザクションを開くと、 Propagation.REQUIREDによって変更された内部方法が周辺法のトランザクションに追加されることを示しています。 Propagation.REQUIREDによって変更されたすべての内部方法と周辺方法。 1つのメソッドがロールバックする限り、トランザクション全体がロールバックされます。
2.propagation_requires_new
Propagation.REQUIRES_NEW属性を、user1serviceとuser2serviceの対応する方法に追加します。
user1Serviceメソッド:
@servicepublic class user1serviceimpl explention user1service {// omit other ... @override @transactional(propagation = propagation.requires_new)public void addRequiresnew(user1 user){user1mapper.insert(user); } @override @transactional(propagation = propagation.required)public void addRequired(user1 user){user1mapper.insert(user); }}user2serviceメソッド:
@servicepublic class user2serviceimpl explention user2service {// omit other ... @override @transactional(propagation = propagation.requires_new)public void addRequiresnew(user2 user){user2mapper.insert(user); } @Override @TransActionAl(Propagation = opagation.Requires_new)public void addRequireNewexception(user2 user){user2mapper.insert(user);新しいruntimeexception()を投げる; }} 2.1シーン1
周辺法は、トランザクションを有効にしません。
検証方法1:
@Override public void nottransaction_exception_requiresnew_requiresnew(){user1 user1 = new user1(); user1.setName( "Zhang San"); user1service.addrequiresnew(user1); user2 user2 = new user2(); user2.setname( "li si"); user2service.addrequiresnew(user2);新しいruntimeexception()を投げる; }検証方法2:
@Override public void nottransaction_requiresnew_requiresnew_exception(){user1 user1 = new user1(); user1.setName( "Zhang San"); user1service.addrequiresnew(user1); user2 user2 = new user2(); user2.setname( "li si"); user2service.addrequiresnewexception(user2); }検証方法を個別に実行すると、結果:
| 検証方法シリアル番号 | データベースの結果 | 結果分析 |
|---|---|---|
| 1 | 「Zhang San」が挿入され、「Li Si」が挿入されます。 | 周辺法にはトランザクションがありません。 「Zhang San」と「Li Si」メソッドの挿入は、独自のトランザクションで独立して実行されます。周辺法の例外ロールバックは、内部方法に影響しません。 |
| 2 | 「Zhang San」が挿入され、「Li Si」は挿入されていません | 周辺法はトランザクションを開始しません。 「Zhang San」メソッドの挿入と「Li Si」メソッドの挿入は、それぞれ独自のトランザクションを開始します。 「li si」メソッドの挿入は例外ロールバックをスローし、他のトランザクションは影響を受けません。 |
結論:これら2つの方法を通じて、 Propagation.REQUIRES_NEWによって変更された内部メソッド。Requires_Newは新しく独自のトランザクションを開始し、オープンしたトランザクションは互いに独立しており、互いに干渉しないことを証明します。
2.2シーン2
周辺法はトランザクションを開始します。
検証方法1:
@override @transactional(propagation = propagation.required)public void transaction_exception_required_requiresnew_requiresnew(){user1 user1 = new user1(); user1.setName( "Zhang San"); user1service.addrequired(user1); user2 user2 = new user2(); user2.setname( "li si"); user2service.addrequiresnew(user2); user2 user3 = new user2(); user3.setname( "wang wu"); user2service.addrequiresnew(user3);新しいruntimeexception()を投げる; }検証方法2:
@override @transactional(propagation = propagation.required)public void transaction_required_requiresnew_requiresnew_exception(){user1 user1 = new user1(); user1.setName( "Zhang San"); user1service.addrequired(user1); user2 user2 = new user2(); user2.setname( "li si"); user2service.addrequiresnew(user2); user2 user3 = new user2(); user3.setname( "wang wu"); user2service.addrequiresnewexception(user3); }検証方法3:
@Override @TransActional(Propagation = opagation.required)public void transaction_required_requiresnew_requiresnew_exception_try(){user1 user1 = new user1(); user1.setName( "Zhang San"); user1service.addrequired(user1); user2 user2 = new user2(); user2.setname( "li si"); user2service.addrequiresnew(user2); user2 user3 = new user2(); user3.setname( "wang wu"); try {user2service.addrequiresnewexception(user3); } catch(Exception e){System.out.println( "Rollingback"); }}検証方法を個別に実行すると、結果:
| 検証方法シリアル番号 | データベースの結果 | 結果分析 |
|---|---|---|
| 1 | 「Zhang San」は挿入されず、「Li Si」が挿入され、「Wang Wu」が挿入されました。 | 周辺法は、トランザクションを開始し、「Zhang San」メソッドと周辺法のトランザクションを挿入し、独立した新たに作成されたトランザクションにそれぞれ「Li Si」メソッドと「Wang Wu」メソッドを挿入します。周辺法は例外をスローし、周辺法と同じトランザクションのみをロールバックするため、「Zhang San」メソッドを挿入する方法が戻ってきます。 |
| 2 | 「Zhang San」は挿入されず、「Li Si」が挿入され、「Wang Wu」は挿入されませんでした。 | 周辺法は、トランザクションを開始し、「Zhang San」メソッドと周辺法のトランザクションを挿入し、独立した新しいトランザクションに「Li Si」メソッドと「Wang Wu」メソッドを挿入します。 「Wang Wu」メソッドが挿入されると、「Wang Wu」メソッドに挿入されるトランザクションがロールバックされます。例外は引き続き投げられ、周辺法によって認識されます。周辺法のトランザクションもロールバックされるため、「Zhang San」メソッドもロールバックされます。 |
| 3 | 「Zhang San」が挿入され、「Li Si」が挿入され、「Wang Wu」は挿入されていません。 | 周辺法は、トランザクションを開始し、「Zhang San」メソッドと周辺法のトランザクションを挿入し、独立した新しいトランザクションに「Li Si」メソッドと「Wang Wu」メソッドを挿入します。 「Wang Wu」メソッドが挿入され、「Wang Wu」メソッドを挿入するトランザクションがロールバックされます。例外は捕らえられ、周辺法によって認識されません。末梢法のトランザクションはロールバックされないため、「Zhang San」メソッドの挿入が正常に挿入されます。 |
結論:周辺法がトランザクションを開くと、 Propagation.REQUIRES_NEWによって変更された内部メソッド。Requires_Newはまだ独立したトランザクションを個別に開き、外部メソッドトランザクションとは無関係です。内部方法、内部方法、および外部メソッドトランザクションは互いに独立しており、互いに干渉しません。
3.propagation_nested
Propagation.NESTED属性を、user1serviceとuser2serviceの対応する方法に追加します。
user1Serviceメソッド:
@servicepublic class user1serviceimpl explence user1service {// omit other ... @override @transactional(propagation = propagation.nested)public void addnested(user1 user){user1mapper.insert(user); }}user2serviceメソッド:
@servicepublic class user2serviceimpl explention user2service {// omit other ... @override @transactional(propagation = propagation.nested)public void addnested(user2 user){user2mapper.insert(user); } @Override @TransActionAl(Propagation = opagation.nested)public void addnestedexception(user2 user){user2mapper.insert(user);新しいruntimeexception()を投げる; }} 3.1シーン1
このシナリオ周辺法は、トランザクションを有効にしません。
検証方法1:
@Override public void nottransaction_exception_nested_nested(){user1 user1 = new user1(); user1.setName( "Zhang San"); user1service.addnested(user1); user2 user2 = new user2(); user2.setname( "li si"); user2service.addnested(user2);新しいruntimeexception()を投げる; }検証方法2:
@Override public void nottransaction_nested_nested_exception(){user1 user1 = new user1(); user1.setName( "Zhang San"); user1service.addnested(user1); user2 user2 = new user2(); user2.setname( "li si"); user2service.addnestedexception(user2); }検証方法を個別に実行すると、結果:
| 検証方法シリアル番号 | データベースの結果 | 結果分析 |
|---|---|---|
| 1 | 「Zhang San」と「Li Si」が両方とも挿入されています。 | 周辺法はトランザクションを開始しておらず、「Zhang San」および「Li Si」メソッドの挿入は、独自のトランザクションで独立して実行されます。異常な周辺法は、「Zhang San」および「Li Si」メソッドの内部挿入に影響しません。 |
| 2 | 「Zhang San」が挿入されますが、「Li Si」は挿入されていません。 | 周辺法にはトランザクションがなく、「Zhang San」と「Li Si」を挿入する方法は両方とも独自のトランザクションで独立して実行されるため、「Li Si」メソッドの挿入は「Li Si」メソッドのみをロールバックし、「Zhang San」メソッドを挿入することは影響を受けません。 |
結論:これらの2つの方法を通して、 Propagation.NESTED and Propagation.REQUIRED 、周辺法がトランザクションを開かない場合に同じ機能を持つことを証明します。変更された内部方法は、独自のトランザクションを再び開始し、オープンしたトランザクションは互いに独立しており、互いに干渉しません。
3.2シーン2
周辺法はトランザクションを開始します。
検証方法1:
@transactional @override public void transaction_exception_nested_nested(){user1 user1 = new user1(); user1.setName( "Zhang San"); user1service.addnested(user1); user2 user2 = new user2(); user2.setname( "li si"); user2service.addnested(user2);新しいruntimeexception()を投げる; }検証方法2:
@transactional @override public void transaction_nested_nested_exception(){user1 user1 = new user1(); user1.setName( "Zhang San"); user1service.addnested(user1); user2 user2 = new user2(); user2.setname( "li si"); user2service.addnestedexception(user2); }検証方法3:
@transactional @Override public void transaction_nested_nested_exception_try(){user1 user1 = new user1(); user1.setName( "Zhang San"); user1service.addnested(user1); user2 user2 = new user2(); user2.setname( "li si"); {user2service.addnestedexception(user2); } catch(Exception e){System.out.println( "Method Rollback"); }}検証方法を個別に実行すると、結果:
| 検証方法シリアル番号 | データベースの結果 | 結果分析 |
|---|---|---|
| 1 | 「Zhang San」と「Li Si」は挿入されていません。 | 周辺法はトランザクションを開始し、内部トランザクションは周辺トランザクションのサブトランザクションです。周辺法はロールバックし、内部方法もロールバックする必要があります。 |
| 2 | 「Zhang San」と「Li Si」は挿入されていません。 | 周辺法はトランザクションを開始し、内部トランザクションは周辺トランザクションのサブトランザクションです。内部メソッドは例外ロールバックをスローし、周辺メソッドは例外を認識し、全体的なトランザクションがロールバックされます。 |
| 3 | 「Zhang San」が挿入され、「Li Si」は挿入されていません。 | 周辺法はトランザクションを開始し、内部トランザクションは周辺トランザクションのサブトランザクションです。 「Zhang San」内部メソッドを挿入して例外をスローすると、子のトランザクションを個別にロールバックできます。 |
結論:上記のテスト結果は、末梢法がトランザクションを開くと、 Propagation.NESTEDによって変更された内部方法が外部トランザクションのサブトランザクションに属していることを示しています。周辺のメイントランザクションがロールバックし、サブトランザクションがロールバックする必要があります。内部サブトランザクションは、周辺のメイントランザクションやその他のサブトランザクションに影響を与えることなく、個別にロールバックできます。
4。必要な類似性と類似性、必要_new、ネスト
「1.2シーン2」と「3.2シーン2」の比較から、次のことがわかります。
ネストされた必要と要求によって変更された内部方法は、両方とも周辺法トランザクションです。周辺法が例外をスローする場合、両方の方法のトランザクションがロールバックされます。ただし、必要なのは周辺法のトランザクションに結合するため、周辺トランザクションと同じトランザクションに属します。必要なトランザクションが例外をスローし、ロールバックされると、周辺法のトランザクションもロールバックされます。 Nestedは周辺法のサブトランザクションであり、個別の保存ポイントを持つため、ネストされた方法は例外をスローし、巻き戻されます。これは周辺法のトランザクションに影響しません。
「2.2シーン2」と「3.2シーン2」の比較から、次のことがわかります。
ネストされたものとrequires_newは、周辺メソッドトランザクションに影響を与えることなく、内部メソッドトランザクションをロールバックできます。ただし、ネストされたものはネストされたトランザクションであるため、周辺法が巻き戻された後、周辺法であるサブトランザクションもロールバックされます。 requires_newは、新しいトランザクションを開くことで実装されます。内部トランザクションと周辺トランザクションは2つのトランザクションです。周辺トランザクションロールバックは、内部トランザクションに影響しません。
5。その他のトランザクション伝播行動
記事の長さの問題を考慮して、他のトランザクション伝播行動のテストはここでは説明されません。興味のある読者は、ソースコードで対応するテストコードと結果の説明を検索できます。ポータル:https://github.com/tmtse/tran ...
シミュレーションユースケース
非常に多くのトランザクション通信行動を導入した後、実際の作業にそれらをどのように適用しますか?例を挙げましょう:
ポイントを追加する方法が呼び出される登録方法があるとします。登録プロセスに影響を与えないようにポイントを追加したい場合(つまり、ロールバックがポイントを追加できなかったため、登録方法もロールバックすることができません)、これを書きます。
@service public class userserviceimpl explence userservice {@transactional public void register(user user){try {membershippointservice.addpoint(point point); } catch(Exception E){// Omit ...} // Omit ...} // Omit ...}また、登録の失敗はaddPoint()メソッド(登録方法ロールバックもロールバックが必要です)に影響するため、 addPoint()メソッドを次のように実装する必要があると規定しています。
@Service Public Class MembershippointServiceImpl MemberShippointService {@TransActionAl(Propagation = opagation.Nested)public void addpoint(point point){try {RecordService.addrecord(レコードレコード); } catch(Exception E){// Omit ...} // Omit ...} // Omit ...} addRecord() addPoint()ことに気付きました。これはログを記録するために使用されます。彼の実装は次のとおりです。
@Service Public Class RecordServiceImpl RecordService {@TransActionAl(Propagation = opagation.Not_Supported)public void addRecord(レコードレコード){// omit ...} // omit ...}} ROGに正確ではなく、多かれ少なかれaddRecord()メソッドと周辺のaddpoint()メソッドは、 addRecord() addPoint()をロールバックしないため、 addRecord()メソッドでaddRecord() addPoint()メソッドでpropagation = Propagation.NOT_SUPPORTEDに気付きました。
この例を通じて、私は誰もがトランザクション通信行動の使用についてより直感的に理解していると信じています。さまざまな属性の組み合わせにより、実際に私たちのビジネスの実装がより柔軟で多様になる可能性があります。
結論は
上記の紹介を通して、私は誰もが春のトランザクション通信行動をより深く理解していると信じており、あなたの毎日の開発作業が役立つことを願っています。
要約します
上記は、この記事のコンテンツ全体です。この記事の内容には、すべての人の研究や仕事に特定の参照値があることを願っています。ご質問がある場合は、メッセージを残してコミュニケーションをとることができます。 wulin.comへのご支援ありがとうございます。