第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
は既存の古いオブジェクトとなっています。obj2
のremember
メソッドが呼び出されると、オブジェクトが有効になり、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_state
と restore_state
メソッドすべてがsuper.save_state
および super.restore_state
を呼び出す必要があります。
12.1.3. パッキングオブジェクト
InputObjectState
や OutputObjectState
で提供されるpack
および unpack
メソッドを利用することで、int や longなどの基本的な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 では、オブジェクト参照を別々にパックおよびアンパックします。