Post Page Advertisement [Top]


JavaではJDBCデータベースへのアクセスする時
自動的にトランザクションが適用されます。

DBは完璧なトランザクションを提供するため
複数のデータの修正や削除などを作業を行う時に、部分のデータが変形することはありません。

こいう一貫性はDBへの信頼に繋がります。







JDBCでデータベースを利用する際に、トランザクションが分かれる選択の条件は二つです。
確定作業のコミットと、本来の状態に戻すロールバックです。

一つのクエリ操作(更新、削除など)が終わると、
トランザクションが終了すると同時に、新しいトランザクション処理の実行に入ります。

トランザクションを説明する時に最もよく使われる例の
銀行口座振込を例に挙げてみます。







JDBCはこの作業を2単位で把握します。

一つ、Aの口座から100万円を差し引い(updateに相当)

二つ、Bの口座に100万円を入れる(updateに該当)

updateが正常に終了した瞬間にコミットが発生します。

その瞬間、トランザクションの単位が終了します。

もしその映像の赤い線の瞬間、エラーが出て作業が強制的に終了した場合は?

一単位のトランザクションが終了したポイントにロールバックされて戻ります。

その瞬間はAの口座から100万円の差し引きが終わった時点です。

Aの立場では、青天の霹靂です。


こいう状況を作らないため、我々は二つの作業単位を一つに結び付ける必要があります。

JavaのJDBCを使用するときに最も使いやすい方法は

コミット設定を切っておくことです。

Connection conn = dataSource.getConnection();

conn.setAutoCommit(false);


コミット操作を取り出しておけば?


コミットは発生しません。
つまり、トランザクションの境界設定の終了しないんです。




もし何らかの理由でConnectionを使用できない場合は、

JdbcTemplateを使ってtransaction synchronizationを利用する方法もあります。








transaction synchronizationとはConnectionオブジェクトを
管理する一つのリポジトリです。

数回のクエリが繰り返されてもコネクションを終了させずに保管しておいた後、次の手順にリサイクルします

commitが呼び出された瞬間トランザクション単位が作られ、transactionSysnchronizationsでようやくConnectionが削除されます。

コードで見ると以下の通りです。
TransactionSynchronizationManager.initSynchronization(); 
Connection conn = DataSourceUtils.getConnection(dataSource); 
conn.setAutoCommit(false);

try{
    // 作業

    conn.commit();
} catch(Exception e) {
    conn.rollback();
} finally {
    DataSourceUtils.releaseConnection(conn, dataSource); 
    TransactionSynchronizationManager.unbindResource(this.dataSource); 
    TransactionSynchronizationManager.clearSynchronization();
}





このような作業の過程とコードでトランザクションの管理をしてくれることができます。

しかし、このような作業も多数のDBを使用する場合は問題が生じます。

トランザクションは、単一のconnectionに依存しているので、

多数のDBを使用する場合トランザクション境界設定ができません。

例えば、DBデータはJDBC、メッセージングサーバーはJMSで管理する場合、

多数の作業をまとめるトランザクションが必要になります。


Javaでは、このような複数のトランザクション(以下、グローバルトランザクション)を管理するAPI、JTA(Java Transaction API)を提供しています。







トランザクションマネージャはXA Protocolを通して
各リソースアダプタと接続してトランザクションを管理します。


これにより、トランザクションマネージャは、両方の管理作業を総合的に制御することができます。


JTAを利用したトランザクションコードは、一般的に以下の通りです。

InitialContext initContext = new InitialContext();

UserTransaction userTran = (UserTransaction)initContext.lookup("jta/usertransaction");
userTran.begin();

Connection conn = dataSource.getConnection();

try {
    // 作業
} catch (Exception e){
    userTran.rollback();
} finally {
    conn.close();
}




JNDIを使用してサーバーのUserTransactionオブジェクトを取った後

JDNIで持って来たdataSourceを利用するコードです。


これにより、ローカルトランザクションが必要な場合の対処方法(transactionSynchronizations)、

グローバル・トランザクションが必要な場合の対処方法(JTA)が揃えました。

しかし、オブジェクト指向の長所は、抽象化を利用し情報の隠匿、再利用性を高めることです。


トランザクションを管理しようとする目的は同じなのに解決策は異なり、

作業内容が変わることは避けるべきです。


このような時のインターフェイスを使い、オブジェクトごとの依存関係を解消できます。


Springでは、トランザクション境界を設定するための抽象インターフェース

PlatformTransactionManagerを提供します。








インターフェイスを使い、個々のコードを変えることなく依存性注入のみで包括的に使うことができます。

このインタフェースを適用したコードは以下の通りです。


PlatformTransactionManager transactionManager = new DataSourceTransactionManager(datasource);
TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
try {
    // 作業
    transactionManager.commit(status);
} catch (Exception e) {
    transactionManager.rollback(status);
} finally {
    transactionManager.rollback(status);
}




transactionManagerオブジェクトの違いだけで、汎用的で変更が必要ないコードが完成しました。

JTAを使うグローバルトランザクションを利用する場合

「new JTATransactionManager();」に変えてくれるだけで結構です。

この処を依存性注入のオブジェクトとして取り扱うと、立派なコードになるでしょう。

これにより、トランザクションと抽象化のポストを終了させて頂きます。

댓글 없음:

댓글 쓰기

Bottom Ad [Post Page]

| Designed by Colorlib