さて、月末は常に激しいもので、 「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(); } });
-
TransactionTemplate
クラスのインスタンスはスレッドセーフであるため、すべてのダイアログ状態がサポートされるわけではありません。 - それでも、
TransactionTemplate
インスタンスは構成状態を維持するため、クラスが異なる設定(たとえば、異なる分離レベル)でTransactionTemplateを使用する必要がある場合、2つの異なるTransactionTemplateインスタンスを作成する必要がありますが、一部のクラスは同じTransactionTemplateインスタンスを使用できます。
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
アノテーション
@Transactional
違いを
@Transactional
ましょう。
@Transactional (isolation=Isolation.READ_COMMITTED)
- デフォルトは
Isolation.DEFAULT
- ほとんどの場合、特別な要件があるまでデフォルト設定を使用します。
- 現在の
tx
次の分離レベルを使用する必要があることをトランザクションマネージャー(tx
)にtx
ます。 txの開始後に分離レベルを変更することはできないため、tx
開始点で設定する必要があります。
DEFAULT :ベースデータベースのデフォルトの分離レベルを使用します。
READ_COMMITTED (固定データの読み取り):ダーティー読み取りが防止されたことを示す定数。 繰り返しのない読み取りと幻の読み取りが発生する場合があります。
READ_UNCOMMITTED (コミットされていないデータの読み取り):この分離レベルは、トランザクションが、他のトランザクションによってまだ削除されていないデータを読み取ることができることを示します。
REPEATABLE_READ (読み取りの再現性):ダーティー読み取りおよび繰り返し不可の読み取りが防止されることを示す定数。 ファントムリーディングが表示される場合があります。
SERIALIZABLE :永続的。ダーティリーディング、繰り返し不可のリーディング、ファントムリーディングが防止されていることを示します。
これらの専門用語は何を意味します:「汚い」読書、幻の読書、または繰り返し読書ですか?
- ダーティリード :トランザクションAが書き込みます。 一方、トランザクション「B」は、トランザクションAが完了するまで同じレコードを読み取りますが、その後、トランザクションAはロールバックを決定し、トランザクションBに互換性のない変更が加えられました。 これはダーティリードです。 トランザクションBは分離レベルREAD_UNCOMMITTEDで動作したため、トランザクションが完了する前にトランザクションAによって行われた変更を読み取ることができました。
- 繰り返し不可の読み取り :トランザクション「A」はいくつかのレコードを読み取ります。 次に、トランザクション「B」がこのレコードを書き込み、コミットします。 トランザクションBはこのレコードに変更を加えてコミットしたため、後でトランザクションAは同じレコードを再度読み取り、異なる値を受け取る場合があります。 これは非繰り返しの読書です。
- ファントム読み取り :トランザクション「A」は一連のレコードを読み取ります。 一方、トランザクション「B」は、トランザクションAと同じ行に新しいレコードを挿入します。その後、トランザクションAは同じ範囲を再度読み取り、トランザクションBが挿入したばかりのレコードも受信します。これは幻の読み取りです。データベースから取得し、さまざまな結果セット(ファントムレコードを含む)を受信しました。
@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
- ターゲットメソッドが呼び出されるたびに新しいtxを実行する必要があることを示します。 txがすでに実行されている場合、新しいtxを開始する前に一時停止されます。
義務
- ターゲットメソッドがアクティブなtxを必要とすることを示します。 txが継続しない場合、失敗せず、例外がスローされます。
サポート
- ターゲットメソッドをtxとは無関係に実行できることを示します。 txが機能する場合、同じtxに参加します。 txなしで実行した場合、エラーがなければ実行されます。
- データを取得するメソッドは、このオプションの最適な候補です。
NOT_SUPPORTED
- ターゲットメソッドがトランザクションコンテキストの伝播を必要としないことを示します。
- 基本的に、トランザクションで実行されるがRAMで操作を実行するメソッドは、このオプションの最適な候補です。
決して
- トランザクションプロセスで実行された場合、ターゲットメソッドが例外をスローすることを示します。
- ほとんどの場合、このオプションはプロジェクトでは使用されません。
@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つが転送され、データベースの一貫性のない状態を終了します。 サービスレベルの注釈は、このような状況からあなたを救うことができます。
この記事がお役に立てば幸いです。
終わり
あなたのコメントや質問を見るのはいつも面白いです。