I.はじめに
トランザクション処理は、最新のソフトウェア開発に不可欠な部分です。複数の操作をまとめて実行する必要がある場合に、例外とエラー状態が回避されるよう事务
にします。数据的完整性和一致性
フレームワークではSpringBoot
、トランザクション処理を管理するために使用できます声明式事务和编程式事务
。その中には、トランザクションにも多くの落とし穴があり、より一般的なのはトランザクションの失敗です。後で、編集者がトランザクションの失敗シナリオに関する記事を公開します. 気に入った場合は、フォローして更新を待つことができます!
このブログでは、ソース コードの実装、相違点、長所と短所、適用可能なシナリオ、およびこれら 2 つのトランザクション処理方法の実際の戦闘に焦点を当てます。
ビジネスについてもお話しましょう。これには 3 つの知識ポイントも含まれます。Baidu で学ぶことができます。
- 取引の特徴
- トランザクションの伝搬動作
- 分離レベル
この記事では主に、トランザクションを実装する 2 つの方法の分析について説明します。
宣言型およびプログラム型トランザクションの探索を始めましょう!
この記事は非常に長いので、辛抱強く読んでお役に立てば幸いです。
この記事のソース コードは次を使用します。springboot2.7.1
2. 使用および概算のソース コードの実装を開く
1. 使い始める
スタートアップ クラスに注釈を追加します。@EnableTransactionManagement
後で使用するために注釈を追加したり@Transactional(rollbackFor = Exception.class)
、プログラムによるトランザクションを使用したりできます。
詳しい使い方は後ほど紹介します!
2. 宣言型トランザクションのソース コード
public class TransactionInterceptor extends TransactionAspectSupport
implements MethodInterceptor, Serializable{
}
TransactionInterceptor UML ダイアグラム:
宣言型トランザクションは主にAOP
実装を通じて実装され、主に次のノードが含まれます。
-
起動時にアノテーションをスキャンする
@Transactional
: 起動時に、Spring Boot は @Transactional でアノテーションが付けられたすべてのメソッドをスキャンし、それらをTransactionAnnotationParser
オブジェクトにカプセル化します。 -
トランザクション管理のコア クラスを実装する AOP はまだ
TransactionInterceptor
. TransactionInterceptor は、@Transactional でアノテーションが付けられたメソッドをインターセプトするためのインターセプターです。 -
TransactionInterceptor をターゲット メソッドに織り込む: AOP プログラミングでは、
AspectJ
アスペクト クラスを使用して@Around
、注釈を通じて TransactionInterceptor をそれに織り込みます目标方法中
。 -
ターゲット メソッドが実行される前にトランザクションを作成する: ターゲット メソッドが実行される前に、TransactionInterceptor を呼び出して
PlatformTransactionManager
新しいトランザクションを作成し、それを現在のスレッドのトランザクション コンテキストに含めます。 -
ターゲット メソッドの実行: ターゲット メソッドの実行時に例外が発生した場合、トランザクション ステータスは としてマークされます
ROLLBACK_ONLY
。それ以外の場合、トランザクション ステータスは としてマークされますCOMMIT
。 -
トランザクションのコミットまたはロールバック: ターゲット メソッドの実行が完了した後、TransactionInterceptor は、トランザクションのステータス (COMMIT または ROLLBACK_ONLY) に従って、トランザクションをコミットするかロールバックするかを決定します。
ソースコード:
@Override
@Nullable
public Object invoke(MethodInvocation invocation) throws Throwable {
// Work out the target class: may be {@code null}.
// The TransactionAttributeSource should be passed the target class
// as well as the method, which may be from an interface.
Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
// Adapt to TransactionAspectSupport's invokeWithinTransaction...
return invokeWithinTransaction(invocation.getMethod(), targetClass, new CoroutinesInvocationCallback() {
@Override
@Nullable
public Object proceedWithInvocation() throws Throwable {
return invocation.proceed();
}
@Override
public Object getTarget() {
return invocation.getThis();
}
@Override
public Object[] getArguments() {
return invocation.getArguments();
}
});
}
以下は、重要度の低いコードを無視し、各ステップのノードを残すコア処理方法です。
@Nullable
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
final InvocationCallback invocation) throws Throwable {
// 获取事务属性
final TransactionManager tm = determineTransactionManager(txAttr);
// 准备事务
TransactionInfo txInfo = prepareTransactionInfo(ptm, txAttr, joinpointIdentification, status);
// 执行目标方法
Object retVal = invocation.proceedWithInvocation();
// 回滚事务
completeTransactionAfterThrowing(txInfo, ex);
// 提交事务
commitTransactionAfterReturning(txInfo);
}
3. プログラムによるトランザクションのソース コード
プログラムによるトランザクションは、主に次のコードに従います。
public class TransactionTemplate extends DefaultTransactionDefinition
implements TransactionOperations, InitializingBean{
}
TransactionTemplate UML ダイアグラム:
TransactionTemplate
このクラスのメソッドは、TransactionCallback オブジェクトのメソッドexecute()
を呼び出すことによって、トランザクションの具体的な実装をカプセル化します。特定の実装では、TransactionTemplate クラスはトランザクションのコミットとロールバックを自動的に制御し、処理のために上位の呼び出し元に例外をスローします。doInTransaction()
执行业务逻辑并管理事务
@Override
@Nullable
public <T> T execute(TransactionCallback<T> action) throws TransactionException {
Assert.state(this.transactionManager != null, "No PlatformTransactionManager set");
if (this.transactionManager instanceof CallbackPreferringPlatformTransactionManager) {
return ((CallbackPreferringPlatformTransactionManager) this.transactionManager).execute(this, action);
}
else {
TransactionStatus status = this.transactionManager.getTransaction(this);
T result;
try {
result = action.doInTransaction(status);
}
catch (RuntimeException | Error ex) {
// Transactional code threw application exception -> rollback
rollbackOnException(status, ex);
throw ex;
}
catch (Throwable ex) {
// Transactional code threw unexpected exception -> rollback
rollbackOnException(status, ex);
throw new UndeclaredThrowableException(ex, "TransactionCallback threw undeclared checked exception");
}
this.transactionManager.commit(status);
return result;
}
}
三、両者の違い
上記はソースコードでの一般的な実装について述べましたが、2 つの違いを紹介しましょう。
- 技術的な実装方法: 宣言型トランザクションは AOP テクノロジによって実現されますが、プログラム型トランザクションは特定のコードを記述することによって実現されます。
- コードの結合: 宣言型トランザクションは、トランザクション処理ロジックをビジネス コードから分離できるため、コードの結合を減らすことができます。プログラムによるトランザクションでは、ビジネス コードでトランザクション管理コードを明示的に呼び出す必要があります。これにより、コードの結合が増加します。
- 難しさ: 宣言型トランザクションは比較的使いやすく、開発者は注釈または XML 構成を学ぶだけで済みます。プログラムによるトランザクションでは、開発者はトランザクション管理の基礎となるメカニズムを理解し、特定のコードを記述する必要があります。
- パフォーマンスへの影響: 宣言型トランザクションはコンテナーによって処理されるため、シナリオによってはパフォーマンスに影響を与える可能性があり、大規模なトランザクションでは多くの問題が発生します (以下では、大規模なトランザクションの問題について説明します)。プログラムによるトランザクションは、トランザクション管理 API を直接呼び出すため、比較的優れたパフォーマンスを発揮します。
一般的に言えば、宣言型トランザクションとプログラム型トランザクションにはそれぞれ長所と短所があり、開発者は特定のニーズに応じてトランザクションを制御する適切な方法を選択する必要があります。
補充:
大規模なトランザクションに過度の時間がかかると、次の問題が発生する可能性があります。:
数据库锁定
トランザクションに大量のデータ操作が含まれる場合、トランザクションはデータベース リソースを占有し、関連するデータを長時間ロックする可能性があります。これにより、他のトランザクションがデータにアクセスしたりデータを変更したりできなくなり、システムの同時実行パフォーマンスとスループットが低下する可能性があります。
资源耗尽
: 実行時間の長いトランザクションは、メモリや CPU など、より多くのシステム リソースを占有する必要があります。システム リソースが不足すると、システムの遅延、デッドロック、システム クラッシュなどの問題が発生する可能性があります。
事务失败概率增加
: トランザクション時間が長すぎると、ネットワーク障害、ハードウェア障害、オペレーティング システムの問題など、トランザクション実行中にさまざまなエラーが発生する可能性があります。この時点で、トランザクションが正常にコミットされず、データの損失またはデータの不整合が発生する可能性があります。
应用程序超时
: 通常、アプリケーションはトランザクションごとにタイムアウトを設定して、長いトランザクション期間を回避します。トランザクションが設定されたタイムアウト期間よりも長く続く場合、アプリケーションはトランザクションの完了を待機することをブロックする可能性があり、最終的にアプリケーションがクラッシュまたはタイムアウトする原因となります。
回滚时间增加
: トランザクションが失敗し、ロールバックが必要な場合、実行時間の長いトランザクションはロールバックに時間がかかります。これにより、データの不整合や損失が発生し、データベース メンテナンスのワークロードが増加する可能性があります。
したがって、開発者は、トランザクション時間が長くなりすぎないようにし、トランザクション スコープを合理的に設定し、トランザクション操作モードを最適化し、データ アクセスの数を減らし、システムの同時実行パフォーマンスとスループットを向上させるその他の手段を講じる必要があります。
プラン:
大きなトランザクションを小さなトランザクションに分割し、クエリを抽出し、運用データベースを抽出してトランザクションに追加できます。
またCompletableFuture
、複合非同期オーケストレーションを使用して、大規模なトランザクションの問題を解決することもできます! !
4. 長所と短所
1. 宣言型トランザクション
宣言型トランザクションは、通常、AOP
メソッドまたはクラス レベルでトランザクション属性を宣言する手法によって実装されます。
宣言型トランザクションの利点は次のとおりです。
简化代码
: 開発者は、手動でトランザクションを管理することなくビジネス ロジックに集中するだけでよく、コードの複雑さと作業負荷を軽減できます。
可配置性强
: トランザクション属性は、柔軟で便利な XML ファイル、注釈などを介して構成できます。
易于扩展
: AOP テクノロジを介して新しいトランザクション戦略をサポートするように簡単に拡張できます。
宣言型トランザクションには、次の欠点があります。
限制较大
: トランザクション属性は、メソッドまたはクラス レベルで宣言する必要があるため、特定のビジネス ニーズを満たすことが困難になる場合があります。
难以调试
: トランザクションは AOP レベルで管理されるため、デバッグ時にトランザクション管理の詳細を追跡するのが難しい場合があります。
2.プログラマティック取引
通常、プログラムによるトランザクションはAPI
インターフェイスを介して実装され、開発者はコードでトランザクションを明示的に管理できます。
プログラマティック トランザクションの利点は次のとおりです。
灵活性强
: 開発者は、特定のビジネス ニーズに従って、コード内のトランザクションの特定のスコープと属性を制御できます。
易于调试
: トランザクション管理はコード レベルで実装されるため、開発者はトランザクション管理の詳細を簡単に追跡できます。
プログラムによるトランザクションには、次の欠点があります。
代码复杂度高
: コード内のトランザクションを手動で処理し、さまざまな例外を処理する必要があるため、コードの複雑さとワークロードが増加する可能性があります。
可配置性差
: トランザクションのスコープと属性は、コードで明示的に宣言する必要があります。これにより、特定のビジネス要件を満たすのが難しくなる場合があります。
結論として、宣言型トランザクションとプログラム型トランザクションの両方に長所と短所があります。開発者は、特定のビジネス ニーズとシナリオに従って、適切なトランザクション管理方法を選択する必要があります。
5. 使用シナリオ
宣言型トランザクションは通常、次のシナリオに適しています。
- 複数のトランザクションを管理する必要がある大規模なエンタープライズ レベルのアプリケーション。
- コード構造は比較的複雑であり、コードをより適切に管理および保守するために宣言型トランザクションを使用できます (大規模なトランザクションについては、上記のソリューションを参照してください)。
- 宣言型トランザクションを使用すると、トランザクション管理をビジネス ロジックから分離できるため、アプリケーションをより疎結合にすることができます。
プログラマティック トランザクションは通常、次のシナリオに適用されます。
- トランザクションのスコープと処理ロジックをより正確に制御する必要があります。
- 通常、プログラムによるトランザクションは宣言型トランザクションよりも柔軟であり、トランザクションのスコープ、分離レベル、およびロールバック メカニズムは、ビジネス ロジックのニーズに応じてカスタマイズできます。
- 一部の同時実行性の高いシナリオでは、ビジネス ロジック全体にトランザクションを追加するのではなく、プログラムによるトランザクションを使用して、操作が必要なデータのみをロックできます。
実際のシナリオでは、選択を行う要件に従って、宣言型トランザクションとプログラム型トランザクションを使用する利点を総合的に検討できます。
さまざまなユーザー ボリュームに応じて選択します。同時実行性がほとんどないシステムで非同期オーケストレーションを設計するのはやり過ぎで、リソースの浪費につながる可能性があります。ただし、必要に応じて、非同期オーケストレーションを使用すると、アプリケーション プログラムがブロックされて API 応答を待機することから解放され等待远程API的响应时
ます等待时间最小化
。 . ユーザー エクスペリエンスを向上させます。
多くのことは絶対的なものではなく、相対的なものであり、既存の通常の使用をサポートできる限り、どのようなデザインでも問題ありません!
優れた設計では、同時実行性の増加を経験する過程でシステムが鈍感になる可能性があります. より良いソリューションを設計し、リソースの浪費を防ぐために、明確に調査する必要があります!
編集者は建築の経験はありませんが、私はまだ建築に興味があります. 建築家になりたくない場合は、良い開発ではありません! ! もちろん経営も取れます!!
六、実戦
1. 宣言型トランザクション
ここに簡単なシミュレーションがあります. エラー レポートをシミュレートするには、OperIp を一意に設定してください!
@Transactional(rollbackFor = Exception.class)
誰もが頻繁に使用するため、デモはもうありません。
@Transactional(rollbackFor = Exception.class)
@Override
public void template() {
SysLog sysLog = new SysLog();
sysLog.setOperIp("123");
SysLog sysLog1 = new SysLog();
sysLog1.setOperIp("hhh");
log.info("插入第一条数据开始========");
testMapper.insert(sysLog);
log.info("插入第一条数据完成========");
log.info("插入第二条数据开始========");
testMapper.insert(sysLog);
log.info("插入第二条数据完成========");
}
この時点では、データにデータはなく、すべてのロールバックが成功しています!
2.プログラマティック取引
最初の注入TransactionTemplate
:
@Autowired
private TransactionTemplate transactionTemplate;
後で直接使用できます。
@Override
public void template() {
SysLog sysLog = new SysLog();
sysLog.setOperIp("123");
SysLog sysLog1 = new SysLog();
sysLog1.setOperIp("hhh");
log.info("插入第一条数据开始========");
testMapper.insert(sysLog);
log.info("插入第一条数据完成========");
transactionTemplate.execute(status -> {
log.info("编程式事务中:插入第一条数据开始========");
testMapper.insert(sysLog1);
log.info("编程式事务中:插入第一条数据完成========");
log.info("编程式事务中:插入第二条数据开始========");
int insert = testMapper.insert(sysLog);
log.info("编程式事务中:插入第二条数据完成========");
return insert;
});
}
データベースをチェックしてください。プログラムによるトランザクションでない場合、最初のデータベースはロールバックに参加しません!
7. まとめ
この記事では、SpringBoot フレームワークにおける宣言型トランザクションとプログラム型トランザクションを紹介し、それらのソース コードの実装、相違点、長所と短所、適用可能なシナリオと実際の戦闘を分析します。
どの方法でトランザクションを管理する場合でも、ビジネス要件と開発チームの実際の状況を考慮し、システムの信頼性と安定性を確保するために適切なトランザクション処理方法を選択する必要があります。
この記事の導入を通じて、宣言型トランザクションとプログラム型トランザクションの概念と原則をよりよく理解し、開発プロセス中に適切なトランザクション処理方法を選択し、プロジェクトの保守性と安定性を向上させることができれば幸いです。
お役に立ちましたら、お金稼ぎの小さな手を動かして、公式アカウントにご注目ください!! ご清聴ありがとうございました!! まずは記事を読もう!! !