Springトランザクション管理
Springトランザクション管理関連インタフェースの関係は次のとおりです。

トランザクション管理の概要
Springは、JDBC、JPA、Hibernate、JTAなどの異なるデータアクセステクノロジのための統一されたトランザクションプログラミングモデルを提供する。ビジネスコードは主にSpringトランザクション抽象化に向けられており、具体的なコミット、ロールバック、リソース管理はトランザクションマネージャによって行われる。
インターフェースはPlatformTransactionManagerです。
public interface PlatformTransactionManager extends TransactionManager {
TransactionStatus getTransaction(TransactionDefinition definition)
throws TransactionException;
void commit(TransactionStatus status) throws TransactionException;
void rollback(TransactionStatus status) throws TransactionException;
}
getTransaction()はトランザクション定義から現在のトランザクション状態を取得します。commit()とrollback()はそれぞれコミットとロールバックを担当します。
Common Transaction Managerについて
JDBCトランザクション[JDBCとらんざくしょん]
JDBCまたはJdbcTemplateを使用する場合、通常はDataSourceTransactionManagerを使用します。
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
同じデータソースから取得されたJDBC Connection経由でトランザクションを管理し、最終的に接続されたcommit()またはrollback()を呼び出します。
JPAのサービス
JPAを使用する場合、通常はJpaTransactionManagerが使用される。
<bean id="transactionManager"
class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
JpaTransactionManagerはJPAのEntityManagerFactory,EntityManagerと連携して事務を管理する.
Hibernateトランザクション#Hibernateトランザクション#
ネイティブのHibernate SessionFactoryを直接使用する場合は、HibernateTransactionManagerを使用できます。プロジェクトでJPA 経由でHibernate 実装を使用する場合は、JpaTransactionManagerを優先してください。
<bean id="transactionManager"
class="org.springframework.orm.hibernate5.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
JTAのサービス
JTAとJtaTransactionManagerは、1つのビジネスオペレーションで複数のトランザクションリソースを調整する必要がある場合に使用できます。例えば、トランザクションは複数のデータベースやデータベースとメッセージングシステムを同時に含む。
<bean id="transactionManager"
class="org.springframework.transaction.jta.JtaTransactionManager"/>
JTAの具体的な構成は、使用されるアプリケーションサーバーまたはトランザクションコーディネータに依存し、すべての環境に1つの固定 Bean 構成だけで対応することはできません。
トランザクションのプロパティ
TransactionDefinitionはトランザクション·ポリシーを記述するために使用され、主に伝播動作、独立性レベル、タイムアウト時間、読み取り専用マーク、トランザクション名を含みます。

public interface TransactionDefinition {
int getPropagationBehavior();
int getIsolationLevel();
int getTimeout();
boolean isReadOnly();
String getName();
}トランザクション伝播の動作{{とらんざくしょんのはこうのどうさ}}
伝播の動作によって、トランザクション付きメソッドが他のトランザクションメソッドによって呼び出された場合に、既存のトランザクションを追加するか、新しいトランザクションを作成するか、トランザクションを禁止するかが決まります。
| 伝播 | 意味 |
|---|---|
REQUIRED | default 値です。トランザクションがあれば参加し、トランザクションがない場合は新規トランザクションを作成する。 |
SUPPORTS | トランザクションがあれば参加し、トランザクションがなければ非トランザクションで実行する。 |
MANDATTORY | トランザクションが存在する必要があります。存在しない場合、例外がスローされます。 |
I_NEW | 常に新しいトランザクションが作成され、既存のトランザクションがある場合は外部トランザクションが一時停止されます。 |
NOT_PORTED | 非トランザクションとして実行します。トランザクションがすでに存在する場合は、トランザクションを一時停止します。 |
NEVER | 非トランザクションで実行する必要があります。トランザクションが存在する場合は例外がスローされます。 |
NESTED | トランザクションがある場合はセーブポイントを使用してネストされたスコープを形成します。トランザクションがない場合はREQUIREDのように動作します。 |
REQUIRED
複数のメソッドが同じ物理トランザクションに参加し、任意の参加者がトランザクションをロールバックのみとしてマークし、最終的にコミットされるとトランザクション全体がロールバックされます。
@Transactional(propagation = Propagation.REQUIRED)
public void saveOrder() {
// 业务逻辑
}
REQUIRES_NEW
内部メソッドは独立したトランザクションを使用し、外部トランザクションは内部トランザクションの実行中に一時停止されます。内部トランザクションがコミットされると、外部トランザクションがロールバックしても、内部コミットされたコンテンツは通常ロールバックされません。
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void saveAuditLog() {
// 独立保存审计日志
}
NESTED
NESTEDは通常、同じ物理トランザクション内のJDBCセーブポイント実装に基づいています。内側のスコープはセーブポイントにロールバックできますが、独立したトランザクションのように外側のトランザクションから分離して最終的にコミットすることはできません。外側のトランザクションがロールバックすると、ネストされたスコープもロールバックされます。
この伝播動作は、トランザクションマネージャと基盤となるリソースのセーブポイントサポートに依存します。JDBCリソーストランザクションによく見られます。
トランザクション独立性レベル{{とらんざくしょんどくりつせいれべる}}
独立性レベルは、同時トランザクション間のデータ可視性を制御するために使用されます。
同時トランザクションに関するFAQ
- ダーティ·リード他のトランザクションがコミットしていないデータを読み込む。
- 繰り返し不可能な読み取り:同じトランザクション内で同じ行を2 回読み取り、異なる値を取得します。通常は他のトランザクションが更新してコミットすることによって引き起こされます。
- ファンタジーリード:同じトランザクション内で同じ条件で2 回クエリされ、結果セット内の行数が変更されます。通常は他のトランザクションが挿入または削除してコミットすることによって引き起こされます。
Springの独立性レベル
| 独立性レベル | 意味 |
|---|---|
| “ | データベースのデフォルト独立性レベルを使用します。 |
_UNCOMMITTED | コミットされていないデータの読み取りが許可されます。ダーティ·リード、繰り返し不可能なリード、マジック·リードが可能です。 |
READ_COMMITTED | コミットされたデータのみを読み取ることができ、ダーティ·リードを回避できます。 |
REPEATABLE_ | 同じトランザクション内でフェッチされたローを繰り返しフェッチするときに一貫性が保たれるため、ダーティ·リードや繰り返し不可能なリードが回避されます。 |
SERIALIZABLE | トランザクションはシリアル実行に似ており、分離度は最も高いが、同時性は通常最も低い。 |
ロックメカニズムと複数バージョンの並行性制御の実装はデータベースによって異なるため、同じ名前の独立性レベルでは詳細が異なる可能性があります。
@Transactional(isolation = Isolation.READ_COMMITTED)
public void updateAccount() {
// 业务逻辑
}
読取り専用トランザクション
読み取り専用マークアップは、トランザクションが主にクエリ操作を実行し、トランザクションマネージャとデータベースが最適化できることを示します。
@Transactional(readOnly = true)
public User queryById(Long id) {
return userMapper.selectById(id);
}
readOnly = trueは通常最適化のヒントであり、絶対的なデータベース書き込みセキュリティ制限として扱わないでください。書き込み操作が本当に禁止されるかどうかは、トランザクションマネージャとデータベースの実装に依存します。
トランザクション·タイムアウト
トランザクション·タイムアウトは、トランザクションが実行できる最大時間を制限するために使用されます。タイムアウト後、トランザクションには通常ロールバックのマークが付けられます。
@Transactional(timeout = 10)
public void importData() {
// 业务逻辑
}
長いネットワーク呼び出し、ファイル処理、またはトランザクション中に人間の待機を行うことは避けてください。データベース接続とロックのリソースを長時間消費します。
ロールバックルール
Spring 宣言型トランザクションはデフォルトでRuntimeExceptionまたはErrorがスローされたときにロールバックされます。通常のチェックされた例外の場合、デフォルトではロールバックされません。
@Transactional(rollbackFor = Exception.class)
public void transfer() throws Exception {
// 发生受检异常时也回滚
}
noRollbackForでは、ロールバックをトリガーしない例外を指定することもできます。
@Transactional(noRollbackFor = BusinessWarningException.class)
public void execute() {
// 业务逻辑
}
ロールバック·ルールはビジネス·セマンティクスに基づいて設定する必要があります。例外を無差別にキャッチした後のサイレント処理は推奨されません。例外がメソッド内でキャッチされてスローされなくなった場合、トランザクションインターセプタは通常例外を認識せず、例外に対して自動的にロールバックしません。
トランザクションのステータス
TransactionStatusは現在のトランザクションの実行状態を示し、セーブポイントやロールバック専用フラグなどの機能を提供します。
public interface TransactionStatus extends TransactionExecution, SavepointManager, Flushable {
boolean hasSavepoint();
void flush();
}
一般的なステータスの方法には、次のものがあります。
isNewTransaction():現在の呼び出しに新しいトランザクションがあるかどうか。hasSavepoint():セーブポイントを保持するかどうか.setRollbackOnly():トランザクションをロールバックのみとしてマークします。isRollbackOnly():トランザクションがロールバックとしてマークされているかどうか。isCompleted():トランザクションが完了しましたか?
インターフェイスの具体的なアプローチはSpringリリースとともに進化し、現在のプロジェクト依存関係のAPIに基づいて使用する必要があります。
プログラマティック·トランザクション
プログラマティックトランザクションは、ビジネスコードによってトランザクション境界を明示的に制御し、トランザクション範囲が動的に決定または細かい制御を必要とするシナリオに適しています。その欠点は、トランザクションコードがビジネスロジックに侵入し、侵入的であることです。
略称はTransactionTemplate
TransactionTemplateは推奨されるプログラマティックトランザクション方式の1つです。
@Service
public class UserService {
private final TransactionTemplate transactionTemplate;
private final UserRepository userRepository;
public UserService(
TransactionTemplate transactionTemplate,
UserRepository userRepository) {
this.transactionTemplate = transactionTemplate;
this.userRepository = userRepository;
}
public Boolean save(User user) {
return transactionTemplate.execute(status -> {
try {
userRepository.insert(user);
return true;
} catch (RuntimeException e) {
status.setRollbackOnly();
throw e;
}
});
}
}コールバックが実行時例外をスローすると、トランザクション·テンプレートはルールに従ってロールバックされます。例外を通常の戻り値に変換する必要がある場合にのみ、setRollbackOnly()を明示的に呼び出す必要があります。
PlatformTransactionManagerを直接使用
DefaultTransactionDefinition definition = new DefaultTransactionDefinition();
definition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
TransactionStatus status = transactionManager.getTransaction(definition);
try {
userRepository.insert(user);
transactionManager.commit(status);
} catch (RuntimeException e) {
transactionManager.rollback(status);
throw e;
}直接操作トランザクションマネージャはより柔軟ですが、コミット、ロールバック、および例外伝播ロジックが正しいことを保証する必要があります。
宣言型トランザクション
宣言型トランザクションはSpring AOPに基づいており、トランザクションのロジックを設定やアノテーションを通じてターゲットメソッドに適用するため、ビジネスコードは手動でコミットやロールバックを必要としません。
XMLの設定方法
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="query*" read-only="true"/>
<tx:method name="get*" read-only="true"/>
<tx:method name="*" propagation="REQUIRED" rollback-for="java.lang.Exception"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="servicePointcut"
expression="execution(* com.example.service..*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="servicePointcut"/>
</aop:config>ルート要素内でtxとaop名前空間を宣言する必要があります。
コメント方式
トランザクション注記を最初に有効にするには、次の手順に従います。
<tx:annotation-driven transaction-manager="transactionManager"/>
Javaを使用して構成することもできます:
@Configuration
@EnableTransactionManagement
public class TransactionConfig {
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
ビジネスクラスまたはメソッドでTransonalを使用する
@Service
public class UserService {
private final UserDao userDao;
public UserService(UserDao userDao) {
this.userDao = userDao;
}
@Transactional(rollbackFor = Exception.class)
public void save(User user) throws Exception {
userDao.insert(user);
}
}クラス上のアノテーションは、そのクラスのメソッドのデフォルトのトランザクション設定として機能します。メソッド上のアノテーションは、クラスレベルの設定をオーバーライドできます。
@Transactional一般的な失敗シナリオ
類似の内部呼び出し
デフォルトのプロキシモードでは、あるメソッドがthis経由で同じクラスの別の@Transactionalメソッドを呼び出すと、呼び出しはSpringプロキシを経由せず、内部メソッドの独立したトランザクション設定は通常有効になりません。
@Service
public class OrderService {
public void createOrder() {
// 这是同类内部调用,不会经过代理对象。
saveOrder();
}
@Transactional
public void saveOrder() {
// 数据库操作
}
}独立したトランザクション境界を必要とするメソッドは、通常、別のSpring Beanに分割する必要があります。
メソッドは非プロキシ
一般的なプロキシモードでは、トランザクションは通常、プロキシで呼び出すことができるpublicインスタンスメソッドに適用されます。privateメソッド、静的メソッド、およびオブジェクトが独自に作成したメソッドは、通常のSpring Beanプロキシとしては機能しません。
オブジェクトがSpring Beanではない
newで手動で作成されたオブジェクトはSpringコンテナによって管理されず、@Transactionalアノテーションは自動的に有効になりません。
異常に捕まった
@Transactional
public void save() {
try {
userDao.insert();
} catch (Exception e) {
// 异常被吞掉后,事务拦截器无法按该异常自动回滚。
}
}
例外を再スローするか、ビジネス上必要に応じてTransactionAspectSupport.currentTransactionStatus().setRollbackOnly()を呼び出す必要があります。
例外型の不一致
チェックされた例外は、デフォルトでロールバックをトリガーしません。ロールバックが必要な場合は、rollbackForで明示的に設定してください。
推奨事項の利用
- トランザクションは通常、コントローラ層ではなくサービス層に配置されます。
- 1つのトランザクションは、一貫性が必要なデータベース操作のみを上書きします。
- トランザクション中の長時間のリモート呼び出しを避ける。
- クエリーメソッドは読み取り専用トランザクションに設定できます。
- ビジネスセマンティクスに基づいて伝播動作を選択し、すべてのメソッドを
REQUIRES_NEWに設定しないでください。 - 例外を変換するときは、元の例外情報を保持し、ロールバックルールが有効であることを確認してください。
気に入ったならばコメントを残してくださいね~