トランザクション管理の実現方法

トランザクション管理をもっともシンプルに実現するためには、コマンドパターンを適用することです。すなわち、コミット時の動作とロールバック時の動作をそれぞれコマンドとして登録しておき、コミットもしくはロールバックが実行されたら、登録された該当するコマンドを実行します。
では、簡単なJavaコードから示します。
まず、コマンドパターンの肝となるコマンドの定義からです。Javaインターフェイスを用いて定義します。

public interface TransactionCommand {
	public void exec();
}

実際のコマンドは、execメソッドにそれぞれコミット、ロールバック時の動作を記述します。
次に、トランザクションを管理するためのクラスを定義します。

import java.util.ArrayList;
import java.util.List;

public class Transaction {
	/** コミット時に実行するコマンド群 */
	private List commits;
	/** ロールバック時に実行するコマンド群 */
	private List rollbacks;
	
	public Transaction(){
		// コマンドを管理するリストを生成します。
		commits = new ArrayList();
		rollbacks = new ArrayList();
	}
	
	public void addCommitCommand( TransactionCommand cmd ){
		// コミット時に実行されるコマンドを登録します。
		commits.add(cmd);
	}
	
	public void addRollbackCommand( TransactionCommand cmd ){
		// ロールバック時に実行されるコマンドを登録します。
		rollbacks.add(cmd);
	}
	
	public void commit(){
		// コミットコマンドを実行し、トランザクションを初期化します。
		for( TransactionCommand c: commits )
			c.exec();
		commits.clear();
		rollbacks.clear();
	}
	
	public void rollback(){
		// ロールバックコマンドを実行し、トランザクションを初期化します。
		for( TransactionCommand c: rollbacks )
			c.exec();
		commits.clear();
		rollbacks.clear();
	}
}

ポイントは、コミット時に実行されるコマンドと、ロールバック時に実行されるコマンドをそれぞれ保持しておき、コミット・ロールバックが呼ばれたら、それらのコマンドを実行するということです。もちろん、コミット・ロールバックが実行されたら、初期化しておきます。
次に、これらを利用するサンプルコードです。教科書とかにありがちなAcountを用いてみました。

public class Acount {
	
	private int acount;
	private Transaction transaction;
	
	public Acount(){
		this.acount = 0;
		this.transaction = new Transaction();
	}
	
	public void add( final int dacount ){
		transaction.addCommitCommand( new TransactionCommand(){
			public void exec() {
				acount += dacount;
			}
		} );
	}
	
	public void sub( final int dacount ){
		transaction.addCommitCommand( new TransactionCommand(){
			public void exec() {
				acount -= dacount;
			}
		} );
	}
	
	public void commit(){
		transaction.commit();
	}
	
	public void rollback(){
		transaction.rollback();
	}
	
	public int getAcount(){
		return acount;
	}
	
}

Acountは、add、subをメインのメソッドとしており、add、subが呼ばれると、内部の数値に与えられた数値を加算・減算します。
ここでは、加算減算時に、Transactionを用いることで、トランザクション管理を実現しています。
次に、Acount実行用テストコードです。

public class AcountTest {
	public static void main( String[] args ){
		Acount acount = new Acount();
		
		System.out.print("最初:");
		System.out.println(acount.getAcount());
		
		acount.add(20);
		acount.add(30);
		acount.add(50);
		System.out.print("20、30、50を足す:");
		System.out.println(acount.getAcount());
		
		acount.commit();
		System.out.print("コミットする");
		System.out.println(acount.getAcount());
		
		acount.sub(20);
		acount.sub(30);
		System.out.print("20、30を引く:");
		System.out.println(acount.getAcount());
		
		acount.rollback();
		System.out.print("ロールバックする:");
		System.out.println(acount.getAcount());
		
	}
}

ここでは、Acountに数値を足したり引いたりし、それぞれのときにcommitやrollbackを行って、Acountの数値がどう変化しているかを観察します。
実行すると、次のような結果を得ます。

最初:0
20、30、50を足す:0
コミットする100
20、30を引く:100
ロールバックする:100

結果より、初期状態0のとき、20、30、50を足しただけでは、内部の値は変化せず、コミットして初めて変化していることがわかります。
また、20、30を引いただけでは内部の値は変化せず、さらにロールバックしてもキチンと元の値を維持していることが伺えます。

まとめ

コマンドパターンを用いた、汎用的なトランザクション管理の実現を示しました。
また、コマンドパターンを用いることで、トランザクション管理を容易に実現できることが分かりました。
最後に、トランザクションを用いた簡単なテストコードを示して、正しく動作することを確認しました。
以上、トランザクション管理のコマンドパターンによる実現方法でした。