唯一の理論と実践は、その後、マスターはそれを忘れて数日と推定、特に強くはありませんしていなかった知っているならば、彼らは何かを学んだ適用することを学び、その後、我々は練習の春のトランザクション伝播特性を学ぶために一緒に来ます。
伝播特性
伝播プロパティが定義されている他の取引方法と、トランザクション処理の振る舞いの方法としては、遭遇した定義され、7つの行動の合計は、次のように
伝達性 | 値 | 説明 |
---|---|---|
PROPAGATION_REQUIRED |
0 | 現在のトランザクション、そうでない場合は、新しいトランザクションをサポートしています |
PROPAGATION_SUPPORTS |
1 | 業務を実行するためではない方法がない場合は、現在のトランザクションをサポートしています |
PROPAGATION_MANDATORY |
2 | 現在のトランザクションが例外をスローしない場合は、現在のトランザクションをサポートしています |
PROPAGATION_REQUIRES_NEW |
3 | 現在のトランザクションがあるかどうかにかかわらず、新しいトランザクションを開始します |
PROPAGATION_NOT_SUPPORTED |
4 | これは、現在のトランザクションが存在する場合、この保留中のトランザクションは、トランザクション・モードで実行されません、トランザクションをサポートしていません。 |
PROPAGATION_NEVER |
5 | 例外をスローするトランザクションがある場合は、トランザクションをサポートしていません。 |
PROPAGATION_NESTED |
6 | 現在のトランザクションは、現在のトランザクション内に存在する場合、その後、新しいトランザクションを開始します |
実際には、単に言葉の概念を見て、我々はあなたに各伝播属性の下の行動の具体例を示し、この時、各スプレッドの役割を非常に簡単に説明されています。
私たちのために、それはちょうど私たちが他の手配をすることなく、トランザクションの効果を証明するように、私たちは、内部のメモリの役割であるH2データベースを使用しているデモは、我々は新しいテーブルを作成します。上の次の文schema.sql
私たちが始めたときである、SpringBootプログラムが自動的にメモリにこのような表を作成することができ、ドキュメント、。
表FOO(ID INT IDENTITY、BAR VARCHAR(64))を作成します。
プレゼンテーションの前に、我々は2つのクラスを定義しますFooService
とBarService
。私たちは、使用BarService
メソッド呼び出しの内側FooService
方法。
環境の準備
トランザクションが実証する前に、実際には、それは次のような状況に分けることができ、順列と組み合わせに応じて、我々は以下の8例を描くことができます
発信者:トランザクションかどうか
発信者:例外があります
呼び出し先:**取引配置で(これは伝播特性によって制御されている)があります**組み合わせはそうではありません
呼び出し先:例外があります
呼び出し側が業務を持っています | 呼び出し側は異常あり | 呼び出し側は異常あり |
---|---|---|
持っています | 持っています | 持っています |
持っています | 持っています | ノー |
持っています | ノー | 持っています |
持っています | ノー | ノー |
ノー | 持っています | 持っています |
ノー | 持っています | ノー |
ノー | ノー | 持っています |
ノー | ノー | ノー |
Exceptionクラス
これはRollbackException
私たち自身の定義の珍しいクラスであります
@Service // www.1b23.com パブリッククラスBarServiceImplはBarService {実装 @Autowired プライベートFooService fooServiceを。 // PROPAGATION_REQUIRED演示无事务 @Override 公共ボイドtestRequiredNoTransactional()スローRollbackException { fooService.testRequiredTransactional()。 } }
呼び出し側
でBarService
取引を持つ2つの方法で、1の定義は、トランザクションはなしです
//有事务 @Override @Transactional(rollbackFor = Exception.class) 公共ボイドhasTransactionalは()RollbackExceptionを{スロー } //无事务 @Override 公共ボイドnoTransactional()がRollbackExceptionをスロー{ }
上記に定義したその後、我々は状況ロシアの8種類に基づいて、トランザクションの伝播特性を学びます。
PROPAGATION_REQUIRED
この伝播特性では、トランザクションはトランザクションと、発信者かどうかに依存し、発信者が新規であるかどうかです。
あなたはこの事実の伝播特性の特性を理解したい二つの例には、我々はこのような状況の8種類を発揮十分です
呼び出し側が業務を持っています | 呼び出し側は異常あり | 呼び出し側は異常あり |
---|---|---|
ノー | ノー | 持っています |
持っています | 持っています | ノー |
クエリがデータを挿入することができないならば、我々は、呼び出し側の場合に例外をスロー最初のケースでは、それは彼らの新しいビジネスの取引せずに、発信者の発信者を示しています。
クエリがデータを挿入することができないならば、我々は、呼び出し側の場合に例外をスロー二例の場合、それは、呼び出し側で呼び出し元がトランザクションに現在のトランザクションに参加している示しています。
のは、呼び出し側のクラスのメソッドの例を見てみましょう。
@Service // www.1b23.com パブリッククラスをFooServiceImpl FooServiceインターフェース{実装 @Autowired プライベートJdbcTemplate jdbcTemplate; // REQUIRED伝播特性-呼び出し先例外がスローされ @Override @Transactional(rollbackFor = Exception.classを伝播= Propagation.REQUIRED ) 公共ボイドtestRequiredHasException()はRollbackException {スロー ; "(+ Global.REQUIRED_HAS_EXCEPTION + ")")jdbcTemplate.execute(VALUES INSERT INTO FOO(BAR)" ;スロー新しい新しいRollbackException() } 異常な呼び出し先- // REQUIRED伝播特性をスロー @Override @Transactional(rollbackFor = Exception.class、伝播= Propagation.REQUIRED) 公共ボイドtestRequiredNoException()RollbackException {スロー jdbcTemplate.execute( ")は" + Global.REQUIRED_NO_EXCEPTION + "(FOO(BAR)VALUES。INSERT INTO")。 } }
次は、呼び出し元のメソッドの例を見て
@Service パブリッククラスをBarServiceImpl実装BarService { @Autowired プライベートFooService fooService; //トランザクションが @Override @Transactional(rollbackFor = Exception.class) 公共ボイドhasTransactional()がスローRollbackException { //呼び出し元のトランザクション、例外をスローせずに、発信者があります異常 fooService.testRequiredNoException(); スロー新しい新しいRollbackException(); } //ないトランザクション @Override 公共ボイドnoTransactional()がRollbackException {スロー //ない取引発信者、発信者は、例外が異常スローされていない (fooService.testRequiredHasExceptionを); } }
この時点で、我々はときに、プログラムの呼び出し照会しました
文字列noException = Global.REQUIRED_NO_EXCEPTION。 文字列hasException = Global.REQUIRED_HAS_EXCEPTION。 {試みる barService.noTransactional()。 }キャッチ(例外e){ log.info( "第一种情况{}"、 jdbcTemplate .queryForObject( "SELECT COUNT(*)BAR = FOO FROM ' "+ hasException +"'"、Long.class))。 } {試みる )(barService.hasTransactionalします。 }キャッチ(例外e){ log.info( "第二种情况{}"、 jdbcTemplate .queryForObject( "SELECT COUNT(*)BAR = FOO FROM ' "+ noException +"'"、Long.class))。 }
ログアウトの表示印刷
2019年10月16日13:02:04.142 INFO 11869 --- [メイン] cettTransactionApplication: 0最初の 2019年10月16日13である:02:04.143 11869 --- INFO [メイン] cettTransactionApplication:第0
我々は、データがロールバックされていることを示すために、対応するデータを特定していないことがわかります。この時点で、私たちは、その文が考えを理解する必要があり、現在のトランザクションをサポートし、そうでない場合は、新しいトランザクション。
PROPAGATION_SUPPORTS
呼び出し側は、呼び出し元に完全に依存して業務を、持って、トランザクションが存在し、呼び出し側は、トランザクションが存在し、呼び出し元はトランザクションは、トランザクションされていないではありません。
次に、我々は、上記の2つの例で実証します
呼び出し側が業務を持っています | 呼び出し側は異常あり | 呼び出し側は異常あり |
---|---|---|
ノー | ノー | 持っています |
持っています | 持っています | ノー |
最初のケース:まだ取引上のデータを照会する場合場合は、発信者が業務を持っていないことを示す、ロールバックされていない、呼び出し元を投げました
第二の場合:発信者がトランザクションに二つの方法でデータかどうかを見つける、例外ケースをスロー
次の例はまだ示してい
呼び出し側が、@Transactional
中に注釈propagation
交換するために、プロパティPropagation.SUPPORTS
被调用者有异常抛出- //は传播属性SUPPORTS @Override @Transactional(rollbackFor = Exception.class、伝播= Propagation.SUPPORTS) 公共ボイドtestSupportsHasExceptionを()RollbackException {スロー jdbcTemplate.execute( "FOO(BAR)値にインサート( ' "+ Global.SUPPORTS_HAS_EXCEPTION +"') "); 新しいRollbackExceptionを投げます(); } //は传播属性サポート-被调用者无异常抛出 @Override @Transactional(rollbackFor = Exception.class、伝播= Propagation.SUPPORTS) 公共ボイドtestSupportsNoException()はRollbackException {スロー jdbcTemplate.execute( "INSERT INTO FOO(BAR)をVALUES( ' "+ Global.SUPPORTS_NO_EXCEPTION +"') "); }
呼び出し元と呼び出し上記の例では、我々は結果を直接実装を参照してください
2019年10月16日13:50:27.738 INFO 12174 --- [メイン] cettTransactionApplication: 。第1の筐体1 2019年10月16日13である:50:27.741 12174 --- INFO [メイン] cettTransactionApplication:第0
私たちは、最初のケースで見つかったデータは、説明はトランザクションではありません最初のケースと呼ばれて参照してください。この時点で、私たちは、この文は理解しておくべきでしょう 事柄を実行しない方法がない場合は、現在のトランザクションをサポートします。
PROPAGATION_MANDATORY
それでもこの2つの例が示します
呼び出し側が業務を持っています | 呼び出し側は異常あり | 呼び出し側は異常あり |
---|---|---|
ノー | ノー | 持っています |
持っています | 持っています | ノー |
最初のケース:トランザクションがないので、発信者なので、このプロパティの普及が異常スローする必要があります
後者の場合:呼び出し側のトランザクションおよびトランザクションが同じ呼び出し側であります
次の呼び出し先のコード例
//強制伝搬特性-呼び出し先例外がスローされ @Override @Transactional(rollbackFor = Exception.class、伝播= Propagation.MANDATORY) 公共ボイドtestMandatoryHasExceptionは()RollbackException {スロー jdbcTemplate.execute(「FOO(BAR)値にインサート( ' "+ Global.SUPPORTS_HAS_EXCEPTION +"')「); スロー新しい新しいRollbackException(); } //強制伝搬特性-例外が呼び出し側にスローされていない @Override @Transactional(rollbackFor = Exception.classを伝播= Propagation.MANDATORY) ボイドtestMandatoryNoException公開は()RollbackException {スロー jdbcTemplate.execute( "INSERT INTO FOO(BAR)値( '" + Global.SUPPORTS_NO_EXCEPTION + "')"); }
呼び出し元と呼び出し上記の例では、我々は結果を直接実装を参照してください
2019年10月16日13:58:39.178 ERROR 12317 --- [メイン] cettTransactionApplication:org.springframework.transaction.IllegalTransactionStateException:トランザクションが見つかりませ既存のトランザクションが伝播が付いていない'必須' 2019年10月16日13:58:39.276 INFO 12317 --- [メイン] cettTransactionApplication:第一种0情况 58:39.281 INFO 12317 --- [メイン] cettTransactionApplication:2019年10月16日13第二种情况0
私たちは同じことを見つけて、我々は、我々は考え、この文を理解しておく必要があり、呼び出し先が彼らの新しい情勢ではないことを示す、推測現在のトランザクションが例外をスローしない場合は、現在のトランザクションをサポートします。
PROPAGATION_REQUIRES_NEW
伝播特性の下で、関係なく、呼び出し元がトランザクションがあるかどうかの、呼び出し元は新しいトランザクションを作成します。
呼び出し側が業務を持っています | 呼び出し側は異常あり | 呼び出し側は異常あり |
---|---|---|
ノー | ノー | 持っています |
持っています | 持っています | ノー |
最初のケース:呼び出し元がないトランザクションは、呼び出し側がそのデータを見つけ、新しいトランザクションを作成します。
後者の場合:呼び出し側のトランザクションがあり、トランザクションが新しい発信者を作成するので、呼び出し側は例外呼び出し側には影響を与えないので、データを見つけることができます
次は、コードをデモ。
呼び出し先
// REQUIRES_NEW传播属性-被调用者有异常抛出 @Override @Transactional(rollbackFor = Exception.class、伝播= Propagation.REQUIRES_NEW) 公共ボイドtestRequiresNewHasExceptionは()RollbackException {スロー jdbcTemplate.execute( "FOO(BAR)値にインサート( ' "+ Global.REQUIRES_NEW_HAS_EXCEPTION +"') "); 新しいRollbackExceptionを投げます(); } // REQUIRES_NEW传播属性-被调用者无异常抛出 @Override @Transactional(rollbackFor = Exception.class、伝播= Propagation.REQUIRES_NEW) 公共ボイドtestRequiresNewNoException()RollbackException {スロー jdbcTemplate.execute( "INSERT INTO FOO(BAR)をVALUES( ' "+ Global.REQUIRES_NEW_NO_EXCEPTION +"') "); }
上記の例と同じ呼び出し側は、我々は直接の実装を見て
2019年10月16日16:29:20.296 INFO 15553 --- [メイン] cettTransactionApplication: 最初のケース0 2019年10月16日16:29:20.298 15553 --- INFO [メイン] cettTransactionApplication:第二ケース
私たちは、そのトランザクションと呼び出し元の呼び出し元のトランザクションは完全に無関係示し、私たちの推論が同じである見つけます。この時点で、私達はちょうどこの文を理解しておく必要がありかかわらず、現在のトランザクションがあるかどうかの、新しいトランザクションを開始します。
PROPAGATION_NOT_SUPPORTED
呼び出し元がトランザクションを持っているかどうか、呼び出し元はトランザクションの方法で実行されていません
また、この2つの例
呼び出し側が業務を持っています | 呼び出し側は異常あり | 呼び出し側は異常あり |
---|---|---|
ノー | ノー | 持っています |
持っています | 持っています | ノー |
最初の場合:発信者が、対応するデータが例外をスローした後に見つけることができ、取引はできません
後者の場合は:,呼び出し側は、呼び出し元の環境事務の不存在下で実行されるトランザクションの状況を持っているので、我々はまだデータに記載されています
そして、私たちの推測を検証
// NOT_SUPPORTED传播属性-被调用者有异常抛出 @Override @Transactional(rollbackFor = Exception.class,propagation = Propagation.NOT_SUPPORTED) public void testNotSupportHasException() throws RollbackException { jdbcTemplate.execute("INSERT INTO FOO (BAR) VALUES ('"+Global.NOT_SUPPORTS_HAS_EXCEPTION+"')"); throw new RollbackException(); } // NOT_SUPPORTED传播属性-被调用者无异常抛出 @Override @Transactional(rollbackFor = Exception.class,propagation = Propagation.NOT_SUPPORTED) public void testNotSupportNoException() throws RollbackException { jdbcTemplate.execute("INSERT INTO FOO (BAR) VALUES ('"+Global.NOT_SUPPORTS_NO_EXCEPTION+"')"); }
然后查看执行结果
2019-10-16 16:38:35.065 INFO 15739 --- [ main] c.e.t.t.TransactionApplication : 第一种情况 1 2019-10-16 16:38:35.067 INFO 15739 --- [ main] c.e.t.t.TransactionApplication : 第二种情况 1
我们可以看到在最后两种情况都查到了数据,根据演示效果应该可以理解这句话了,不支持事务,如果当前存在事务,就将此事务挂起不以事务方式运行。
PROPAGATION_NEVER
调用者有事务,被调用者就会抛出异常
调用者是否有事务 | 调用者是否有异常 | 被调用者是否有异常 |
---|---|---|
无 | 无 | 有 |
有 | 有 | 无 |
这个就不演示,相信大家看到这里应该都会明白在第一种情况下我们是能够查到数据的。在第二种情况下由于调用者带着事务,所以会抛异常。
PROPAGATION_NESTED
此传播属性下,被调用者的事务是调用者的事务的子集。
我们重点说一下NESTED
的传播属性的特性
调用者是否有事务 | 说明 |
---|---|
有 | 被调用者会新起一个事务,此事务和调用者事务是一个嵌套的关系 |
无 | 被调用者会自己新起一个事务 |
关于什么是嵌套事务的关系,我们用下面三个例子能够进行演示。
调用者是否有事务 | 调用者是否有异常 | 被调用者是否有异常 |
---|---|---|
无 | 无 | 有 |
有 | 有 | 无 |
有 | 无 | 有 |
第一种情况:如果查不到数据,则说明在调用者无事务情况下,被调用者会新起一个事务
第二种情况:如果查不到数据,说明外层事务能够影响内层事务
第三种情况:如果查到数据,说明内层事务不影响外层事务
接下来我们编写具体的代码
// NESTED传播属性-回滚事务 @Override @Transactional(rollbackFor = Exception.class,propagation = Propagation.NESTED) public void testNestedHasException() throws RollbackException { jdbcTemplate.execute("INSERT INTO FOO (BAR) VALUES ('"+Global.NESTED_HAS_EXCEPTION+"')"); // TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); throw new RollbackException(); } // NESTED传播属性-不回滚事务 @Override @Transactional(rollbackFor = Exception.class,propagation = Propagation.NESTED) public void testNestedNoException() throws RollbackException { jdbcTemplate.execute("INSERT INTO FOO (BAR) VALUES ('"+Global.NESTED_NO_EXCEPTION+"')"); }
然后接下来的调用者也会有点区别
@Override @Transactional() public void hasTransactionalNoException() throws RollbackException { // NESTED传播属性 - 调用者有事务,不抛异常 被调用者有异常 jdbcTemplate.execute("INSERT INTO FOO (BAR) VALUES ('"+Global.NESTED_HAS_EXCEPTION_TWO+"')"); fooService.testNestedHasException(); }
然后执行效果
2019-10-16 18:01:06.387 INFO 17172 --- [ main] c.e.t.t.TransactionApplication : 第一种情况 0 2019-10-16 18:01:06.389 INFO 17172 --- [ main] c.e.t.t.TransactionApplication : 第二种情况 0 2019-10-16 18:01:06.390 INFO 17172 --- [ main] c.e.t.t.TransactionApplication : 第三种情况 1
可以看出来嵌套事务的本质就是外层会影响内层,内层不影响外层。而REQUIRES_NEW
则是互不影响。
总结
到现在我们已经全部分析完了七种传播属性,从写这篇文章开始到结束其中也碰到过一些坑,有些是不自己实践一遍是根本不知道的,所以我还是建议读者看完这篇文章以后自己进行实践,演示各种情况,只有这样才能够烂熟于心。