1.アフェアーズ
トランザクション管理システムの開発には、不可欠な部分であるSpring
優れたトランザクション管理メカニズムを提供し、それが分割され编程式事务
、および声明式事务
2種類。
プログラムによるトランザクション:次の例のように、コードでのトランザクション送信、ロールバック、およびその他の操作の手動管理を指します。コードは比較的煩わしいものです。
try {
//TODO something
transactionManager.commit(status);
} catch (Exception e) {
transactionManager.rollback(status);
thrownew InvoiceApplyException("异常失败");
}
宣言型トランザクション:AOP
アスペクト指向に基づいて、特定のビジネスとトランザクション処理の部分を分離します。コードの侵入は非常に少ないため、実際の開発では宣言型トランザクションがより多く使用されます。また、二つの方法で実装宣言型トランザクションは、1をベースにTX
し、AOP
XML設定ファイルの方法、の2種類に基づいて@Transactional
、注釈付き。
@Transactional
@GetMapping("/test")
public String test() {
int insert = cityInfoDictMapper.insert(cityInfoDict);
}
2. @Transactionalの概要
1. @Transactionalアノテーションはどこで機能しますか?
@Transactional
それは行動することができ接口
、类
、类方法
。
- クラスに対するアクション:
@Transactional 注解放在类上时,表示所有该类的
public メソッドが同じトランザクション属性情報で構成されている場合。 - メソッドへの影響:クラスが構成される
@Transactional
と、メソッドも構成@Transactional
されます。メソッド のトランザクションは、クラスのトランザクション構成情報をオーバーライドします。 - インターフェースでの動作:この使用方法はお勧めできません。インターフェースでマークを付け、Spring AOPでCGLib動的プロキシを使用するように構成すると、
@Transactional
アノテーションが無効になるためです。
@Transactional
@RestController
@RequestMapping
publicclass MybatisPlusController {
@Autowired
private CityInfoDictMapper cityInfoDictMapper;
@Transactional(rollbackFor = Exception.class)
@GetMapping("/test")
public String test() throws Exception {
CityInfoDict cityInfoDict = new CityInfoDict();
cityInfoDict.setParentCityId(2);
cityInfoDict.setCityName("2");
cityInfoDict.setCityLevel("2");
cityInfoDict.setCityCode("2");
int insert = cityInfoDictMapper.insert(cityInfoDict);
return insert + "";
}
}
2. @Transactionalアノテーションの属性は何ですか?
伝播属性
propagation
トランザクションの伝播動作を表します。デフォルト値は Propagation.REQUIRED
次のとおりです。その他の属性情報は次のとおりです。
Propagation.REQUIRED
:現在トランザクションがある場合はトランザクションに参加し、現在のトランザクションがない場合は新しいトランザクションを作成します。( つまり、メソッドAとメソッドBの両方に注釈が付けられている場合、デフォルトの伝播モードでは、メソッドAがメソッドBを内部的に呼び出し、2つのメソッドのトランザクションが1つのトランザクションにマージされます )Propagation.SUPPORTS
:現在トランザクションがある場合はトランザクションに参加し、トランザクションがない場合は非トランザクション的な方法で実行を続けます。Propagation.MANDATORY
:現在トランザクションがある場合はトランザクションが追加され、現在のトランザクションがない場合は例外がスローされます。Propagation.REQUIRES_NEW
:新しいトランザクションを再作成します。現在トランザクションがある場合は、現在のトランザクションを一時停止します。( クラスAPropagation.REQUIRED
のメソッドaがデフォルトモード、クラスBのメソッドbにPropagation.REQUIRES_NEW
モードを加えた 後、メソッドaでメソッドbを呼び出してデータベースを操作しますが、メソッドaが例外をスローした後、メソッドbはロールバックしません。Propagation.REQUIRES_NEW
メソッドa )のトランザクションを一時停止する ためPropagation.NOT_SUPPORTED
:非トランザクションの方法で実行します。現在トランザクションがある場合は、現在のトランザクションを一時停止します。Propagation.NEVER
:非トランザクションの方法で実行します。トランザクションが現在存在する場合は、例外がスローされます。Propagation.NESTED
:Propagation.REQUIREDと同じ効果。
分離特性
isolation
:トランザクションの分離レベルIsolation.DEFAULT
。デフォルト値は です。
- TransactionDefinition.ISOLATION_DEFAULT: バックエンドデータベースのデフォルトの分離レベル、MysqlのデフォルトのREPEATABLE_READ分離レベル、OracleのデフォルトのREAD_COMMITTED分離レベルを使用します。
- TransactionDefinition.ISOLATION_READ_UNCOMMITTED: 最も低い分離レベル。コミットされていないデータ変更の読み取りを許可します。これにより、ダーティリード、ファントムリード、または繰り返し不可の読み取りが発生する可能性があります
- TransactionDefinition.ISOLATION_READ_COMMITTED: 並行トランザクションによって送信されたデータの読み取りを許可し、ダーティリードを防ぐことができますが、マジックリードまたは繰り返し不可の読み取りが発生する可能性があります
- TransactionDefinition.ISOLATION_REPEATABLE_READ: 独自のトランザクションによってデータが変更されない限り、同じフィールドでの複数の読み取りの結果は一貫しており、ダーティリードや繰り返し不可の読み取りを防ぐことができますが、マジックリードは引き続き発生します。
- TransactionDefinition.ISOLATION_SERIALIZABLE: ACID分離レベルに完全に準拠した最高の分離レベル。すべてのトランザクションは1つずつ順番に実行されるため、トランザクション間の干渉の可能性はありません。つまり、このレベルでは、ダーティリード、繰り返し不可の読み取り、マジックリードを防ぐことができます。しかし、これはプログラムのパフォーマンスに深刻な影響を及ぼします。通常、このレベルは使用されません。
タイムアウト属性
timeout
:トランザクションのタイムアウト期間。デフォルト値は-1です。時間制限を超えてもトランザクションが完了していない場合、トランザクションは自動的にロールバックされます。
readOnly属性
readOnly
:トランザクションが読み取り専用トランザクションかどうかを指定します。デフォルト値はfalseです。データの読み取りなど、トランザクションを必要としないメソッドを無視するには、読み取り専用をtrueに設定できます。
rollbackFor属性
rollbackFor
:トランザクションのロールバックをトリガーできる例外のタイプを指定するために使用されます。複数の例外タイプを指定できます。
noRollbackFor属性**
noRollbackFor
:指定した例外タイプをスローします。トランザクションをロールバックせずに複数の例外タイプを指定することもできます。
3つの@Transactional障害シナリオ
次に、特定のコードと組み合わせた特定のコードを分析してみましょう。@ Transactionalアノテーションは無効になります。
1. @Transactionalは非公開の装飾メソッドに適用されます
Transactional
注釈がpublic
装飾されていないメソッドに適用された場合、トランザクションは無効になります。
無効な理由は、Spring AOPプロキシでは、上図TransactionInterceptor
(トランザクションインターセプター)に示すように 、ターゲットメソッドの実行前後にDynamicAdvisedInterceptor
、インターセプトメソッド(CglibAopProxyの内部クラス)または JdkDynamicAopProxy
invokeメソッドが間接的AbstractFallbackTransactionAttributeSource
にcomputeTransactionAttribute
メソッドを呼び出し て トランザクションを取得するためです。注釈付きトランザクション構成情報。
protected TransactionAttribute computeTransactionAttribute(Method method,
Class<?> targetClass) {
// Don't allow no-public methods as required.
if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
returnnull;
}
このメソッドは、ターゲットメソッドの修飾子がpublicであるかどうか、@Transactional
およびpublicでない場合は取得されないプロパティ構成情報をチェックします。
注:protected
、private
修正された方法の使用して @Transactional
、トランザクションが有効ではありませんが、コメントを、しかし、エラーは存在しません、これは間違いを犯すために私たちの非常に少ない容量です。
2. @Transactionalアノテーションプロパティの伝播が正しく設定されていない
この失敗は構成エラーが原因であり、次の3つの構成が正しく構成されていない場合、トランザクションはロールバックされません。
TransactionDefinition.PROPAGATION_SUPPORTS
:現在トランザクションがある場合はトランザクションに参加し、現在トランザクションがない場合は非トランザクションの方法で実行を続けます。
TransactionDefinition.PROPAGATION_NOT_SUPPORTED
:非トランザクションモードで実行し、現在トランザクションがある場合は、現在のトランザクションを一時停止します。
TransactionDefinition.PROPAGATION_NEVER
:非トランザクションモードで実行します。トランザクションが現在存在する場合は、例外がスローされます。
3. @Transactionalアノテーション属性rollbackForが正しく設定されていない
rollbackFor
トランザクションのロールバックをトリガーできる例外のタイプを指定できます。デフォルトでは、Springは未チェックのunchecked
例外(継承された RuntimeException
例外)をスローするか Error
、トランザクションをロールバックします;他の例外はロールバックトランザクションをトリガーしません。トランザクションで他のタイプの例外がスローされても、Springがトランザクションをロールバックできると予想される場合は、rollbackFor属性を指定する必要があり ます。
// 希望自定义的异常可以进行回滚
@Transactional(propagation= Propagation.REQUIRED,rollbackFor= MyException.class
ターゲットメソッドでスローrollbackFor
された例外が指定された例外のサブクラスである 場合、トランザクションもロールバックされます。Springのソースコードは次のとおりです。
private int getDepth(Class<?> exceptionClass, int depth) {
if (exceptionClass.getName().contains(this.exceptionName)) {
// Found it!
return depth;
}
// If we've gone as far as we can go and haven't found it...
if (exceptionClass == Throwable.class) {
return -1;
}
return getDepth(exceptionClass.getSuperclass(), depth + 1);
}
4.同じクラスのメソッド呼び出しにより、@ Transactionalが失敗する
開発中、同じクラスのメソッドを呼び出すことは避けられません。たとえば、メソッドTestがあり、そのメソッドA、Aの1つは、このクラスのメソッドBを呼び出します(メソッドBがパブリックまたはプライベートのどちらで変更されたかに関係なく)、メソッドAステートメントはトランザクションに注釈を付け、Bメソッドはそうします。メソッドAを外部から呼び出した後、メソッドBのトランザクションは機能しません。ここも間違いが多い場所です。
なぜこれが起こるのですか?実際、これはSpring AOP
プロキシの使用が原因です。トランザクションメソッドが現在のクラス以外のコードによって呼び出された場合のみ、Spring
生成されたプロキシオブジェクトによって管理されるためです。
//@Transactional
@GetMapping("/test")
private Integer A() throws Exception {
CityInfoDict cityInfoDict = new CityInfoDict();
cityInfoDict.setCityName("2");
/**
* B 插入字段为 3的数据
*/
this.insertB();
/**
* A 插入字段为 2的数据
*/
int insert = cityInfoDictMapper.insert(cityInfoDict);
return insert;
}
@Transactional()
public Integer insertB() throws Exception {
CityInfoDict cityInfoDict = new CityInfoDict();
cityInfoDict.setCityName("3");
cityInfoDict.setParentCityId(3);
return cityInfoDictMapper.insert(cityInfoDict);
}
5.例外がキャッチによって「食べられ」、@ Transactionalが失敗する
この状況は、最も一般的なタイプの @Transactional
注釈失敗シナリオです。
@Transactional
private Integer A() throws Exception {
int insert = 0;
try {
CityInfoDict cityInfoDict = new CityInfoDict();
cityInfoDict.setCityName("2");
cityInfoDict.setParentCityId(2);
/**
* A 插入字段为 2的数据
*/
insert = cityInfoDictMapper.insert(cityInfoDict);
/**
* B 插入字段为 3的数据
*/
b.insertB();
} catch (Exception e) {
e.printStackTrace();
}
}
メソッドBが内部で例外をスローし、メソッドAがこの時点でメソッドBの例外をキャッチしようとした場合、このトランザクションは正常にロールバックできますか?
回答:いいえ!
例外をスローします:
org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only
ときので、ServiceB
後に例外がスローされServiceB
、現在のトランザクションのニーズを特定しますrollback
。ただしServiceA
、この例外を手動でキャッチして処理するため、ServiceA
現在のトランザクションは正常であるはずcommit
です。このとき、矛盾がありました。つまり、このため、前のUnexpectedRollbackException
例外がスローされました。
spring
トランザクションは、ビジネスメソッドが呼び出される前、commit
または ビジネスメソッドが実行された後に開始rollback
されます。トランザクションが実行されるかどうかは、それがスローされるかどうかによって異なりruntime异常
ます。スローさruntime exception
れ、ビジネスメソッドにキャッチがない場合、トランザクションはロールバックされます。
通常、ビジネスメソッドでは例外のキャッチは必要ありません。キャッチする必要があるthrow new RuntimeException()
場合は、例外をスローするか、アノテーションの例外タイプを指定する必要があります@Transactional(rollbackFor=Exception.class)
。それ以外の場合は、トランザクションが失敗し、データのコミットによりデータの不整合が発生するため、キャッチを試行するとさらに追加されます。
6.データベースエンジンはトランザクションをサポートしていません
この状況の可能性は高くありません。トランザクションが有効になるかどうかは、データベースエンジンがトランザクションをサポートするかどうかの鍵となります。一般的に使用されるMySQLデータベースはinnodb
、デフォルトでトランザクションサポートエンジンを使用します。データベースエンジンがトランザクションをサポートしないように切り替えられるとmyisam
、トランザクションは基本的に無効になります。
まとめ
@Transactional
注釈はシンプルで使いやすいように見えますが、その使用法について少し知らなければ、まだ多くの穴を踏むことになります。