第12章 ヒント

12.1. 一般的なコツ

12.1.1. コンストラクタでトランザクションを利用

本書の例は新規永続オブジェクトの実装でトランザクションを使っています。こうすることで、オブジェクトステートをオブジェクトに正しく伝播できるようにします。トップレベルトランザクションのコミット時に、変更した永続オブジェクトの状況のみがオブジェクトストアに記述されます。そのため、コンストラクタのトランザクションがトップレベルでコミットすると、新規作成されたオブジェクトがストアに記述され、すぐに利用できるようになります。ただし、オブジェクトが作成される前に起動していた別のトランザクションが稼働しているためにコンストラクタのトランザクションがコミットされるにも拘らずネスト化されている場合は、親トランザクションがコミットされると状況のみが記述されます。
一方で、コンストラクタがトランザクションを使用しない場合、システム内に矛盾が発生してしまう場合があります。例えば、オブジェクト作成時にトランザクションが有効でない場合、トランザクションの制御化でオブジェクトが次に変更されるまで、トランザクションのステートはストアに保存されません。

例12.1 トランザクションが原因でシステムの不整合が発生

AtomicAction A = new AtomicAction();
Object obj1;
Object obj2;

obj1 = new Object();			// create new object
obj2 = new Object("old");		// existing object

A.begin(0);
obj2.remember(obj1.get_uid());	// obj2 now contains reference to obj1
A.commit(true);				// obj2 saved but obj1 is not
ここでは、トップレベルのアクションAの制御外にオブジェクトを2つ作成されており、obj1は新規オブジェクトで、obj2 は既存の古いオブジェクトとなっています。obj2remember メソッドが呼び出されると、オブジェクトが有効になり、obj1 のUid が分かります。このアクションがコミットされると、obj2 の永続ステートにはobj1のUid が含まれるようになるでしょう。しかし、アクション制御により操作されていないため、obj1のステートはまだ保存されていません。実際、アプリケーション内で何らかのアクションの管理下で変更が加えられない限り、保存はされません。しかし、コンストラクタがアトミックアクションを利用している場合、obj1 の状況は構築時に自動的に保存され、矛盾を防ぎます。

12.1.2. save_state および restore_state メソッドに関する詳細情報

JBoss Transaction Serviceは、オブジェクトコンストラクタのボディを実行するときなどオブジェクトが有効であればいつでも、オブジェクトにあるユーザ定義のsave_state メソッドを呼び出すことができます。特にアトミックアクションを利用する場合に当てはまります。save_stateで保存した変数はすべて、正しく初期化されます。
save_state および restore_state メソッドを記述する際は、トランザクションが明示的あるいは暗黙的に開始されないよう十分に注意してください。JBoss Transaction Service は、処理中のcommit の一部としてrestore_state メソッドを呼び出し、別トランザクションのcommit あるいは abort フェーズ中にアトミックトランザクションを実行してしまう可能性があるためです。これにより、コミットあるいは中断されたトランザクションのアトミックプロパティが妨害されてしまう可能性があるため、推奨されません。
永続オブジェクトの障害回復をサポートするには、ユーザオブジェクトのsave_staterestore_state メソッドすべてがsuper.save_state および super.restore_stateを呼び出す必要があります。

12.1.3. パッキングオブジェクト

InputObjectStateOutputObjectStateで提供されるpack および unpack メソッドを利用することで、intlongなどの基本的なJava のタイプを全て保存し、InputObjectState あるいは OutputObjectState からリストア可能となっています。しかし、オブジェクトのパッキングによりエイリアスの別問題が発生してしまうため、オブジェクトのパッキングとアンパッキングについては違った方法で処理する必要があります。エイリアスとは、2種のオブジェクト参照が実際は同じアイテムを参照している可能性があることを意味します。例12.2「オブジェクトのパッキングにおけるエイリアスの問題」を参照してください。

例12.2 オブジェクトのパッキングにおけるエイリアスの問題

public class Test
{
    public Test (String s);
    ...
	private String s1;
    private String s2;
};

public Test (String s)
{
    s1 = s;
    s2 = s;
}
ここでは、s1およびs2は同じstringを指定し、save_state メソッドのナイーブな実装がこの string を2度コピーする可能性があります。save_state メソッドから見ると、これは非効率なだけですが、restore_state メソッドがこれら2つの string を別のメモリ領域にアンパックし、元のエイリアス情報を壊してしまうのです。JBoss Transaction Service では、オブジェクト参照を別々にパックおよびアンパックします。