Springでの効率的なトランザクション管理

すべての人に良い一日を!

さて、月末は常に激しいもので、 「Developer on Spring Framework」コースの第2ストリームの開始まであと1日しかありません。これは、同じように美しく怒りのユリ (一部の学生は要件のレベルでDZで)、私たちがあなたのために準備した別の資料を見てみましょう。

行こう

はじめに

ほとんどの場合、開発者はトランザクション管理を重要視しません。 その結果、ほとんどのコードを後で書き換える必要があるか、開発者が実際にどのように機能するか、具体的にどのような側面を使用するかを知らずにトランザクション管理を実装します。

トランザクション管理の重要な側面は、トランザクションを開始するタイミングと終了するタイミング、データベースにデータを追加するタイミング、ポンプバックするタイミング(例外の場合)のトランザクションの正しい境界を決定することです。



開発者にとって最も重要な側面は、アプリケーションにトランザクション管理を最適に実装する方法を理解することです。 それでは、さまざまなオプションを見てみましょう。

トランザクション管理方法

トランザクションは次の方法で管理できます。

1.カスタムコードを記述することによるプログラム制御

これは古いトランザクション管理方法です。

EntityManagerFactory factory = Persistence.createEntityManagerFactory("PERSISTENCE_UNIT_NAME"); EntityManager entityManager = entityManagerFactory.createEntityManager(); Transaction transaction = entityManager.getTransaction() try { transaction.begin(); someBusinessCode(); transaction.commit(); } catch(Exception ex) { transaction.rollback(); throw ex; } 

長所


短所


2. Spring to Transactional Managementの使用

Springは2種類のトランザクション管理をサポートしています

1.ソフトウェアトランザクション管理 :プログラミングを通じてトランザクションを管理する必要があります。 この方法は十分に柔軟性がありますが、保守が困難です。

2.宣言的なトランザクション管理 :トランザクション管理をビジネスロジックから分離します。 トランザクション管理には、XMLベースの構成でのみ注釈を使用します。

宣言的なトランザクションを使用することを強くお勧めします。 理由を知りたい場合は読み進めてください。そうでない場合は、このオプションを実装する場合は、宣言型トランザクション管理セクションに直接進んでください。

それでは、各アプローチを詳しく見ていきましょう。

2.1。 プログラムによるトランザクション管理:

Springフレームワークは、プログラムによるトランザクション管理のための2つのツールを提供します。

a。 TransactionTemplate使用(Springチームが推奨):

次のサンプルコードを使用してこのタイプを実装する方法を見てみましょう(Springのドキュメントから一部変更を加えたものです)

コードスニペットはSpring Docsから取られていることに注意してください。

コンテキストXmlファイル:

 <!-- Initialization for data source --> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/TEST"/> <property name="username" value="root"/> <property name="password" value="password"/> </bean> <!-- Initialization for TransactionManager --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean> <!-- Definition for ServiceImpl bean --> <bean id="serviceImpl" class="com.service.ServiceImpl"> <constructor-arg ref="transactionManager"/> </bean> 

Serviceクラス:

 public class ServiceImpl implements Service { private final TransactionTemplate transactionTemplate; //       PlatformTransactionManager public ServiceImpl(PlatformTransactionManager transactionManager) { this.transactionTemplate = new TransactionTemplate(transactionManager); } //       ,   ,    //       xml  this.transactionTemplate.setIsolationLevel(TransactionDefinition.ISOLATION_READ_UNCOMMITTED); this.transactionTemplate.setTimeout(30); //30  ///    public Object someServiceMethod() { return transactionTemplate.execute(new TransactionCallback() { //         public Object doInTransaction(TransactionStatus status) { updateOperation1(); return resultOfUpdateOperation2(); } }); }} 

戻り値がない場合、以下に示すように、匿名クラスで便利なTransactionCallbackWithoutResultクラスを使用します。

 transactionTemplate.execute(new TransactionCallbackWithoutResult() { protected void doInTransactionWithoutResult(TransactionStatus status) { updateOperation1(); updateOperation2(); } }); 


b。 PlatformTransactionManager実装を直接使用する:

コードでこのオプションをもう一度見てみましょう。

 <!-- Initialization for data source --> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/TEST"/> <property name="username" value="root"/> <property name="password" value="password"/> </bean> <!-- Initialization for TransactionManager --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean> public class ServiceImpl implements Service { private PlatformTransactionManager transactionManager; public void setTransactionManager( PlatformTransactionManager transactionManager) { this.transactionManager = transactionManager; } DefaultTransactionDefinition def = new DefaultTransactionDefinition(); //     -  ,       def.setName("SomeTxName"); def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); TransactionStatus status = txManager.getTransaction(def); try { //   -  } catch (Exception ex) { txManager.rollback(status); throw ex; } txManager.commit(status); } 

次のトランザクション管理方法に進む前に、選択するトランザクション管理のタイプを決定する方法を見てみましょう。

プログラムによる トランザクション管理宣言的なトランザクション管理の選択:


2.2。 宣言的トランザクション(通常、Webアプリケーションのほぼすべてのシナリオで使用されます)

ステップ1 :スプリングアプリケーションのコンテキストxmlファイルでトランザクションマネージャーを定義します。

 <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"/> <tx:annotation-driven transaction-manager="txManager"/> 

ステップ2 :スプリングアプリケーションのコンテキストxmlファイルにエントリを追加して、アノテーションサポートを有効にします。

または、以下に示すように、構成ファイルに@EnableTransactionManagementを追加します。

 @Configuration @EnableTransactionManagement public class AppConfig { ... } 

Springでは、特定のクラス(および特定のクラスのメソッド)のみに@Transactional注釈を付けることと、インターフェースに注釈を付けることを推奨しています。

これは、インターフェイスレベルで注釈を配置し、プロキシクラス( proxy-target-class = «true» )または絡み合ったアスペクト( mode = «aspectj» )を使用すると、トランザクションパラメータがプロキシインフラストラクチャで認識されないためです。また、トランザクション動作などの神経叢は適用されません。

ステップ3@Transactional注釈をクラス(クラスメソッド)またはインターフェイス(インターフェイスメソッド)に追加し@Transactional

 <tx:annotation-driven proxy-target-class="true"> 

デフォルト構成: proxy-target-class="false"


それでは、 @Transactionalアノテーション@Transactional違いを@Transactionalましょう。

@Transactional (isolation=Isolation.READ_COMMITTED)


DEFAULT :ベースデータベースのデフォルトの分離レベルを使用します。

READ_COMMITTED (固定データの読み取り):ダーティー読み取りが防止されたことを示す定数。 繰り返しのない読み取りと幻の読み取りが発生する場合があります。

READ_UNCOMMITTED (コミットされていないデータの読み取り):この分離レベルは、トランザクションが、他のトランザクションによってまだ削除されていないデータを読み取ることができることを示します。

REPEATABLE_READ (読み取りの再現性):ダーティー読み取りおよび繰り返し不可の読み取りが防止されることを示す定数。 ファントムリーディングが表示される場合があります。

SERIALIZABLE :永続的。ダーティリーディング、繰り返し不可のリーディング、ファントムリーディングが防止されていることを示します。

これらの専門用語は何を意味します:「汚い」読書、幻の読書、または繰り返し読書ですか?


@Transactional(timeout=60)

デフォルトは、基になるトランザクションシステムのデフォルトのタイムアウトです。

応答しないトランザクションをロールバックするかどうかを決定する前に、txがアイドルになるまで待機する時間の長さをtxマネージャーに通知します。

@Transactional(propagation=Propagation.REQUIRED)

指定しない場合、伝播するデフォルトの動作はREQUIREDです。

その他のオプションはREQUIRES_NEW, MANDATORY, SUPPORTS, NOT_SUPPORTED, NEVERおよびREQUIRES_NEW, MANDATORY, SUPPORTS, NOT_SUPPORTED, NEVERです。

必須

アクティブなtxがないと、ターゲットメソッドが機能しないことを示します。 このメソッドを呼び出す前にtxがすでに実行されている場合、同じtxで継続するか、このメソッドを呼び出した直後に新しいtxが開始されます。

REQUIRES_NEW


義務


サポート



NOT_SUPPORTED


決して


@Transactional (rollbackFor=Exception.class)

デフォルト値: rollbackFor=RunTimeException.class

Springでは、すべてのAPIクラスがRuntimeExceptionをスローします。つまり、メソッドが失敗した場合、コンテナーは常に現在のトランザクションをロールバックします。

問題はチェックされた例外のみです。 したがって、このパラメーターを使用して、 Checked Exceptionが発生した場合にトランザクションを宣言的にロールバックできます。

@Transactional (noRollbackFor=IllegalStateException.class)

ターゲットメソッドがこの例外を発生させた場合、ロールバックが発生しないことを示します。

さて、トランザクション管理の最後の、しかし最も重要なステップは、 @Transactiona Transactiona lアノテーションの投稿です。 ほとんどの場合、注釈を配置する場所は混乱しています:サービスレベルで、またはDAOレベルで?

@Transactional :サービスまたはDAOレベル?

サービスは@Transactionalを配置するのに最適な場所です。サービスレベルには、論理的にトランザクションに入るユーザーインタラクションの詳細レベルでのユースケースの動作を含める必要があります。

コントローラーとデータアクセスオブジェクト間でデータを単純に転送するサービスレベルを持つ重要なビジネスロジックを持たないCRUDアプリケーションは多くありますが、これは有用ではありません。 このような場合、トランザクションアノテーションをDAOレベルに配置できます。

したがって、実際には、どこにでも配置できますが、それはあなた次第です。

さらに、 @TransactionalをDAOレイヤーに配置し、DAOレイヤーを異なるサービスで再利用する場合、サービスごとに要件が異なる可能性があるため、DAOレイヤーに配置するのは困難です。

サービスレベルがHibernateを使用してオブジェクトを取得し、ドメインオブジェクトの定義に遅延初期化がある場合、サービスレベルでトランザクションを開く必要があります。そうしないと、ORMによってスローされるLazyInitializationExceptionが発生します。

サービスレベルが2つの異なるDAOメソッドを呼び出してデータベース操作を実行できる別の例を考えてみましょう。 最初のDAO操作が失敗した場合、他の2つが転送され、データベースの一貫性のない状態を終了します。 サービスレベルの注釈は、このような状況からあなたを救うことができます。

この記事がお役に立てば幸いです。

終わり

あなたのコメントや質問を見るのはいつも面白いです。

Source: https://habr.com/ru/post/J431508/


All Articles