この章では、Sun社のEnterprise JavaBeans(EJB)アーキテクチャの概要およびそのOracle Application Serverへの実装について説明します。
Enterprise JavaBeans(EJB)は、オブジェクト指向のJavaアプリケーションを開発およびデプロイするための、標準的なサーバー・サイド・コンポーネント・アーキテクチャです。これによって、開発者は、分散アプリケーションを短時間で簡単に構築できるようになります。
EJBアーキテクチャの主な目的は、ソース・コードとバイナリ・コードの両方のレベルでのコンポーネントの移植性を提供することです。
EJBコンポーネント(Enterprise Bean)は、Javaで記述されたサーバー・サイド・コンポーネントで、通常、アプリケーションのビジネス・ロジックを含んでいます。表5-1に、様々なタイプのEnterprise Beanのサマリーを示します。
表5-1 EJBのタイプ
Enterprise Beanのタイプ | 説明 |
---|---|
セッションBean |
1つのクライアントに代わってサービスを提供するために作成されるコンポーネント。1つのクライアント/サーバー・セッションが継続されている間のみ存続します。 |
エンティティBean |
データ・ストアに保持されているデータを表すコンポーネント。表しているデータが存在するかぎり存続できます。 |
EJBアーキテクチャでは、コンポーネントの移植性が提供されていますが、EJBコンポーネントの実装固有の特定の機能は移植できません。 これらの機能には、次のものがあります。
Enterprise Beanのデプロイ
デプロイされたEnterprise Beanに対するランタイム・サポート
エンティティBeanのコンテナ管理の永続性
EJB導入の目的の1つは、異なる環境間でのコンポーネントの移植性を、ソース・コード・レベルのみでなくバイナリ・レベルでも提供し、パッケージ化されたコンパイル済コンポーネントの移植性を確保することです。EJBには高い移植性がありますが、多くの移植不可能な実装固有の機能もあります。これらには、アプリケーション・サーバー間でコンポーネントを移行する際に対処する必要があります。通常、EJBコンポーネントでは、コンテナとの低レベルのインタフェースが必要です。このインタフェースは、常にコンテナ実装固有の状態にしておく必要があるスタブおよびスケルトン・クラスで構成されています。実際、EJBコンポーネントの移植可能な要素と移植不可能な要素は、EJB 1.1仕様によって明確に区別できます。
移植可能なEJB要素には、次のものがあります。
実装固有の要素には、次のものがあります。
CMPエンティティBeanのオブジェクト・リレーショナル・マッピングの定義(各アプリケーション・サーバー独自の実装固有の形式で宣言されたカスタム・ファインダ・メソッド用の検索ロジックなど)。
すべてのコンポーネントに、デプロイ時に系統立てて構成する必要がある一連のプロパティが含まれています。たとえば、EJBコンポーネントに宣言されているセキュリティ・ロールを実際のユーザーおよびグループにマップするタスクは、デプロイ時に系統立てて実行します。その理由として、まず、マッピングは事前に確認できないこと、次に、ターゲット・デプロイ・サーバー上のユーザー・ディレクトリの構造および要素に依存関係が存在することがあげられます。
JBossからOC4Jへの移行に固有の問題があります。これらの問題は、EJB標準サポートのレベルが異なることおよびAPIに対するJBoss固有の拡張機能が存在することが原因で発生します。JBoss 3.2.6ではEJB 1.0仕様、OC4JではEJB 2.0仕様がサポートされています。
後続の各項では、EJB仕様、セッションBean、エンティティBean、トランザクションと同時実行性、前述のAPIとJBossの拡張機能に対するJBossでのサポート、OC4JとJBossでのEJBコンテナの相違点、およびOC4Jへの移行パスについて説明します。
EJBテクノロジは、エンタープライズ・システムにとって重要な次の新機能を統合をすることによって、基本的なJavaオブジェクト・モデルの機能を強化することを目的としています。
オブジェクトのセキュリティ: オブジェクトを使用できるユーザー、そのユーザーが使用できるオブジェクトおよびそのオブジェクトの使用方法
オブジェクトの永続性: オブジェクトを長期的に格納する方法
オブジェクトのトランザクションの動作
分散: リモート・アプリケーションからオブジェクトにアクセスする方法
開発者から見ると、EJBは、次のもので構成されるファイルのグループです。
Javaクラス
Javaインタフェース
デプロイメント情報
メタデータ
EJBサーバーでは、低レベルのシステム・リソースが管理され、必要に応じてリソースがコンテナに割り当てられます。EJBサーバーは、EJBコンテナのホストとして機能し、EJBコンテナにランタイム環境を提供します。コンテナは、クライアントに対して透過的です。つまり、コンテナを操作するためのクライアントAPIは存在せず、Enterprise Beanがデプロイされているコンテナをクライアントで識別する方法はありません。ただし、EJBコンテナとEJBサーバーは構造的に明確に分離されていません。EJB仕様では、Beanとコンテナの契約のみが定義されており、コンテナとサーバーの契約は定義されていません。
EJBコンテナは、Enterprise Beanがデプロイされる特殊なサービスです。EJBコンテナによって、デプロイされたEnterprise Beanが、基礎となるEJBサーバーから分離され、Beanとコンテナ間に標準的なApplication Program Interface(API)が提供されます。この特殊サービスでは、EJB仕様に示されている特定の契約および制約モデルに従って、ライフ・サイクル、トランザクション、セキュリティ、ネーミング、永続性などが管理されます。これを行うには、サーバーで提供されている汎用サービスをコンテナで使用します。
Enterprise JavaBeans仕様では、分散アプリケーションの開発での特定のタスクに関連する次のロールが指定されています。
通常、アプリケーション分野(金融業界、電気通信業界など)を専門に扱います。Beanプロバイダは、ビジネス・タスクを実装します。アプリケーションの分散、トランザクション、セキュリティおよびその他のビジネス以外の側面には関与しません。
これも、特定の分野を専門に扱います。アプリケーション・アセンブラは、既成の様々な構築ブロック(Enterprise Bean)からアプリケーションを作成し、GUIクライアント、アプレット、サーブレットなどの他のコンポーネントを追加してアプリケーションを完成します。アプリケーションの作成時、アセンブラは、Enterprise Beanに対するインタフェースのみに関与します。Enterprise Beanの実装には関与しません。
デプロイヤは、アプリケーションのインストールを専門に実行します。デプロイヤは、Enterprise Beanのプロパティを変更することによって、多数のEnterprise Beanで構成されたアプリケーションをターゲットの動作環境に適合せます。たとえば、デプロイヤが実行するタスクには、トランザクション・ポリシーとセッション・ポリシーの設定、デプロイメント・ディスクリプタのプロパティを適切に設定することによるJNDI名の指定、エンタープライズ管理ソフトウェアとの統合などがあります。
通常、分散されたインフラストラクチャおよびサービスを専門とするベンダーです。サーバー・プロバイダは、分散アプリケーションの開発を簡略化し、それらのアプリケーションのランタイム環境を提供するプラットフォームを実装します。このロールでは、レガシー・アプリケーションまたはレガシー・システムの特定のクラスをラッピングする特殊なコンテナを提供することもできます。
セッションBeanは、1つのクライアントに代わって実行されるオブジェクトです。コンテナでは、クライアントからのリモート・タスク・リクエストへの応答として、セッションBeanのインスタンスが作成されます。セッションBeanのクライアントは1つのみです。つまり、セッションBeanはEJBサーバー内のクライアントを表しています。セッションBeanは、トランザクション対応でもあります。基礎となるデータベースの共有データをセッションBeanで更新することはできますが、その共有データベース・データを直接表しているわけではありません。セッションBeanの存続期間は、比較的短期間で一時的なものです。通常、セッションBeanは、クライアントがセッションの対話を維持している間存続します。クライアントが対話を終了すると、そのクライアントへのセッションBeanの関連付けも終了します。セッションBeanは、一時的であるとみなされます。コンテナがクラッシュした場合、セッションBeanのインスタンスが削除され、クライアントで処理を続行するには新しいセッション・オブジェクトを再度確立する必要があるためです。
セッションBeanには、ステートフル・セッションBean(SFSB)とステートレス・セッションBean(SLSB)の2つのタイプがあります。これらのBeanでは、いずれもjavax.ejb.SessionBean
を実装する必要あります。ただし、EJBコンテナ内ではこれらのセッションBeanのライフ・サイクルは異なります。
通常、セッションBeanでは、クライアントとの相互作用(対話)の状態が維持されます。つまり、セッションBeanでは、クライアント・セッションが継続されている間、複数のメソッド起動にまたがってクライアントに関する情報が保持されます。状態を維持するセッションBeanは、ステートフル・セッションBeanと呼ばれます。クライアントがセッションBeanとの対話を終了すると、セッションは終了し、Beanで状態の値が維持されなくなります。
セッションBeanのライフ・サイクルは、そのBeanのホーム・インタフェースで定義されているcreate()
メソッドがクライアントによって起動された時点から始まります。このメソッドの起動に応答して、コンテナで次の処理が実行されます。
セッションBeanインスタンス用に新しいメモリー・オブジェクトが作成されます。
セッションBeanのsetSessionContext()
メソッドが起動されます。このメソッドによって、セッション・コンテキスト・インタフェースへの参照がセッションBeanインスタンスに渡されます。セッションBeanインスタンスでは、その参照を使用してコンテナ・サービスを取得し、クライアントによって起動されたメソッドのコール元に関する情報を取得することができます。
EJBクライアントによってコールされたcreate()
メソッドに対応する、セッションBeanのejbCreate()
メソッドが起動されます。
セッションBeanインスタンスの作成後、そのライフ・サイクルは準備状態になります。この状態では、リモート・インタフェースに定義されているBeanのビジネス・メソッドをEJBクライアントで起動することができます。この状態でのコンテナのアクションは、メソッドの起動がトランザクションに基づいているかどうかによって異なります。
クライアントでトランザクション・ビジネス・メソッドを起動すると、セッションBeanインスタンスがトランザクションに関連付けられます。Beanインスタンスがトランザクションに関連付けられると、その関連付けはトランザクションが完了するまで保持されます。また、EJBクライアントで同じBeanインスタンスに対して別のメソッドの起動を試行し、それによってBeanインスタンスが別のトランザクションに関連付けられるか、またはいずれのトランザクションにも関連付けられなくなった場合は、エラーが発生します。その場合、コンテナによって次のメソッドが起動されます。
afterBegin()
メソッド(セッションBeanによってSessionSynchronization
インタフェースが実装されている場合)
Beanのリモート・インタフェースに定義されており、EJBクライアントによってコールされたビジネス・メソッドに対応するBeanクラス内のビジネス・メソッド
BeanインスタンスのbeforeCompletion()
メソッド(セッションBeanによってSessionSynchronization
インタフェースが実装されている場合)
次に、トランザクション・サービスでトランザクションのコミットが試行され、その結果、コミットまたはロールバックのいずれかが実行されます。トランザクションが完了すると、コンテナによってBeanのafterCompletion()
メソッド(BeanによってSessionSynchronization
インタフェースが実装されている場合)が起動され、トランザクションの完了ステータス(コミットまたはロールバック)がafterCompletion()
メソッドに渡されます。
ロールバックが発生した場合、ステートフル・セッションBeanでは、トランザクションが開始される前にBeanインスタンスに含まれていた値まで対話状態をロールバックできます。ステートレス・セッションBeanでは、対話状態が維持されないため、ロールバックには関与しません。
コンテナには、メモリー内に保持しておくEnterprise Beanインスタンスを管理するために、高度なアルゴリズムが含まれています。コンテナによって、ステートフル・セッションBeanインスタンスがメモリー内に必要なくなったと判断されると、そのBeanインスタンスのejbPassivate()
メソッドが起動され、そのBeanインスタンスは予約プールに移動されます。ステートフル・セッションBeanインスタンスがトランザクションに関連付けられている場合、そのインスタンスはパッシブ化(非アクティブ化)できません。
クライアントで、ステートフル・セッションBeanのパッシブ化されているインスタンスに対してメソッドを起動すると、コンテナによって、インスタンスの状態がリストアされた後、BeanインスタンスのejbActivate()
メソッドが起動されて、インスタンスのアクティブ化が行われます。このメソッドが戻されると、Beanインスタンスは再度準備状態になります。
特定のタイプのすべてのステートレス・セッションBeanインスタンスは、そのタイプの他のすべてのインスタンスと同じであるため、ステートレス・セッションBeanインスタンスはパッシブ化もアクティブ化もされません。これらのインスタンスは、削除されるまで常に準備状態で存在します。
ステートフル・セッションBeanのライフ・サイクルは、そのBeanのホーム・インタフェースまたはリモート・インタフェースで定義されているremove()
メソッドがEnterprise Beanクライアントまたはコンテナによってコールされた時点で終了します。このメソッド起動に応答して、コンテナによってBeanインスタンスのejbRemove()
メソッドが起動されます。コンテナでは、このメソッドによってステートレス・セッションBeanを終了するか、または後で使用できるようにステートレス・セッションBeanをプールしておくことができます。
コンテナでは、EJBオブジェクトの存続期間が期限切れになった後、インスタンスに対して削除メソッドを暗黙的にコールできます。セッションEJBオブジェクトの存続期間は、タイムアウト属性を使用してデプロイメント・ディスクリプタに設定します。
セッションBeanがステートレス・セッションBeanの場合もあります。ステートレス・セッションBeanでは、クライアントの情報または状態は維持されません。クライアントは、なんらかの目的を実現するためにステートレス・セッションBeanのメソッドを起動できますが、メソッド・コールが継続されている間のみ、Beanによってインスタンス変数に値が保持されます。メソッドの終了後、ステートレス・セッションBeanに値(または状態)は保持されません。したがって、メソッドの起動中以外、すべてのステートレス・セッションBeanインスタンスは同じになります。そのため、ステートレス・セッションBeanでは、複数のクライアントをサポートできます。コンテナでは、ステートレスBeanインスタンスのプールを維持し、任意のインスタンスを任意のクライアントに割り当てることができます。
ステートレス・セッションBeanのライフ・サイクルには、次の2つの状態があります。
非存在状態
メソッド起動可プール状態
Beanインスタンスが非存在状態の場合は、インスタンス化が行われていないことを意味します。コンテナによってBeanインスタンスが作成され、クライアント・リクエストを処理する準備ができると、そのインスタンスはメソッド起動可プール状態になります。コンテナで次の3つの操作が実行されると、ステートレス・セッションBeanが非存在状態からメソッド起動可プール状態に移行します。
ステートレスBeanクラスでClass.newInstance()
メソッドが起動されます。
BeanインスタンスでSessionBean.setSessionContext(SessionContext context)
メソッドが起動されます。
BeanインスタンスでejbCreate()
メソッドが起動されます。
エンティティBeanは、ドメイン・モデルに保持されている永続データのオブジェクト・ビュー、およびそのデータに対して実行されるメソッドを表します。具体的には、エンティティBeanはドメイン・モデルのレコードにマッピングされます。リレーショナル・データベースのコンテキストでは、表内の各行に1つのBeanが存在します。各エンティティBeanは、主キーによって識別されます。エンティティBeanは、オブジェクト・ファクトリのcreate()
メソッドを使用して作成します。エンティティBeanへのアクセスは、複数のクライアントで共有できます。つまり、1つのエンティティBeanに複数のクライアントが同時にアクセスできます。エンティティでは、データの整合性が維持されるように、基礎となるデータに対するアクセスおよび更新がトランザクションのコンテキスト内で行われます。また、エンティティBeanは、暗黙的に永続的にもなります。EJBオブジェクトの永続性は、EJBオブジェクト自体で管理するか、またはコンテナに委任することができるためです。エンティティBeanは、永続性のタイプに基づいて次の2つのタイプに分類されます。
コンテナ管理の永続性(CMP)によって、開発者は、開発中に永続性を直接処理せずにEJBコンポーネントを構築できます。CMPエンティティBeanでは、EJBコンテナによって、エンティティBeanの状態の維持、永続ストア(データベース)内のインスタンス・フィールドの同期が行われます。たとえば、データベースに対して読取りおよび書込みを行うSQLコードの生成および実行の両方がコンテナによって管理されることになります。これはコンテナ管理であるため、データソースとは関係なく実装できます。コンテナで永続性を自動的に処理するには、コンテナ管理のすべてのフィールドをデプロイメント・ディスクリプタに指定する必要があります。CMPエンティティBeanは、永続データ用のラッパーであり、通常、トランザクション制御およびセキュリティの追加サポートが備えられたリレーショナル・データベース表の形式になっています。
BMPエンティティBeanでは、エンティティBean自体によってその状態が維持されるため、コンテナによってデータベース・コールを生成する必要はありません。各BMP EJBでは、そのライフ・サイクル中に適宜送信される特定のフック・メッセージ(ejbLoad()
、ejbStore()
など)に応答して、バックエンドのデータストアへの状態の格納およびバックエンドのデータストアからの状態の取得が行われます。したがって、この実装では、永続性をBeanにハードコードする必要があるため、柔軟性がCMPより低くなります。
エンティティBeanは、長期間存続するとみなされ、その状態は永続的です。エンティティBeanは、アプリケーションまたはサーバーのプロセスが実行されている間ではなく、データがデータベース内に存在するかぎり存続します。エンティティBeanは、EJBコンテナがクラッシュしても影響を受けません。Enterprise Beanがコンテナにデプロイされた後、クライアントは、必要に応じてそのBeanのインスタンスを作成および使用できます。コンテナ内で、Enterprise Beanのインスタンスは、定義されているライフ・サイクルを経ます。Enterprise Beanのライフ・サイクル中のイベントは、クライアントまたはコンテナのいずれかによって開始されたアクションから導出されます。エンティティBeanのライフ・サイクルには、次の3つの状態があります。
この段階では、Beanのインスタンスは存在しません。エンティティBeanインスタンスのライフ・サイクルは、コンテナによってそのインスタンスが作成された時点から始まります。新しいエンティティBeanインスタンスが作成された後、コンテナによってそのインスタンスのsetEntityContext()
メソッドが起動されます。このメソッドによって、エンティティ・コンテキスト・インタフェースへの参照がBeanインスタンスに渡されます。Beanインスタンスでは、その参照を使用して、コンテナ・サービスの取得、およびクライアントによって起動されたメソッドのコール元に関する情報の取得を行うことができます。
作成されたエンティティBeanインスタンスは、指定されたエンティティBeanクラスの使用可能なインスタンスのプールに格納されます。インスタンスは、このプール内にある間は、特定のEJBObject
に関連付けられません。このプール内の同じEnterprise Beanクラスのすべてのインスタンスは同じになります。インスタンスがプール状態になっている間は、コンテナでこのインスタンスを使用して任意のBeanのファインダ・メソッドを起動できます。
クライアントで特定のエンティティBeanインスタンスを処理する必要がある場合は、コンテナによって、プールからインスタンスが選択され、そのインスタンスがクライアントによって初期化されたEJBObject
と関連付けられます。準備状態の使用可能なインスタンスが存在しない場合は、エンティティBeanインスタンスがプール状態から準備状態に移行されます。
エンティティBeanインスタンスは、次の2つの場合にプール状態から準備状態に移行されます。
エンティティBeanクラスの新しい一意のエンティティ(およびデータソース内の新しいレコード)を作成するために、クライアントがBeanのホーム・インタフェースでcreate()
メソッドを起動した場合。このメソッドを起動すると、コンテナによって、BeanインスタンスのejbCreate()
およびejbPostCreate()
メソッドがコールされます。新しいEJBObject
が、Beanインスタンスに関連付けられます。
エンティティBeanクラスの(データソース内の既存のレコードに関連付けられている)既存のインスタンスを操作するためにクライアントがファインダ・メソッドを起動した場合。この場合、コンテナによって、BeanインスタンスのejbActivate()
メソッドがコールされ、Beanインスタンスは既存のEJBObject
に関連付けられます。
Enterprise Beanインスタンスが準備状態になっている場合は、コンテナでBeanインスタンスのejbLoad()
およびejbStore()
メソッドを起動して、インスタンス内のデータをデータソース内の対応するデータと同期させることができます。また、インスタンスがこの状態になっている場合は、クライアントでBeanのビジネス・メソッドを起動することもできます。エンティティBeanインスタンスのビジネス・メソッドを適切なトランザクション(または非トランザクション)方式で処理するために必要なすべての対話は、EJBの開発者がこれらの対話をBean自身で処理するようにBeanを記述していないかぎり、コンテナによって処理されます。コンテナによって、準備状態のエンティティBeanインスタンスが必要なくなったと判断されると、そのインスタンスはプール状態に移行されます。このようなプール状態への移行は、次のいずれかの場合に発生します。
コンテナによって、ejbPassivate()
メソッドが起動された場合。
クライアントによって、Beanインスタンスに関連付けられているEJBObject
、またはEJBホーム・オブジェクトでremove()
メソッドが起動された場合。remove()
メソッドがコールされると、基礎となるエンティティがデータソースから完全に削除されます。
エンティティBeanで表される状態は共有され、トランザクションに基づいています。一方、セッションBeanで状態が保持されている場合、その状態はプライベートで、対話的である必要があります。
永続性の問題は複雑であり、この件に関しては、多くの研究プロジェクトが進行中です。重要な点の1つに、実行時のオブジェクト(EJB)は独立したエンティティではなく、相互に参照されているということがあげられます。このため、永続性の問題では、独立したオブジェクトではなく、複雑なオブジェクト・グラフが対象となります。解決が必要な多くの問題があります。メモリー内のオブジェクト・グラフをディスクに投影するにはどうすればよいでしょうか。また、その逆はどうでしょうか。ディスク上のグラフとメモリー内のグラフ間の同期の問題を解決するにはどうすればよいでしょうか。任意の時点で使用されているグラフの一部のみをメモリーにロードするにはどうすればよいでしょうか。グラフをリレーショナル・データベース(オブジェクト・リレーショナル・マッピング・テクノロジ)内に保存するにはどうすればよいでしょうか。
EJB仕様では、エンティティBeanとその永続性表現を明確に分離することによって、コンテナ管理の永続性を実現しようとしています。つまり、データ・ロジック・メソッド(2つのフィールドをまとめて追加するためのエンティティBean内のロジックなど)とJDBCの分離です。この分離が有効である理由には、エンティティBeanのロジックに影響を与えずにエンティティBeanの永続的表現への変更(リレーショナル・データベースからオブジェクト・データベースへの変更など)を行うできることがあげられます。
この明確な分離を実現するには、コンテナ管理の永続性を備えたエンティティBeanクラスを、JDBCなどの永続性ロジックが含まれないように記述する必要があります。これによって、コンテナでエンティティBeanクラスがサブクラス化され、JDBCが生成されます。生成されたサブクラスには、元のエンティティBeanクラスが継承されます。したがって、すべてのコンテナ管理の永続性を備えたエンティティBeanは、それぞれが(ユーザーが記述し、エンティティBeanデータ・ロジックが含まれている)スーパークラスおよび(コンテナによって生成され、永続性ロジックが含まれている)サブクラスという2つのクラスに分類されます。これらの2つのクラスによって、エンティティBeanのロジックと永続性表現が明確に分離されます。実際のエンティティBeanは、スーパークラスとサブクラスが組み合されたものです。
トランザクションは、1つの単位として処理する必要がある一連の文です。トランザクションには、ACIDという頭字語で表される4つの特性(原子性、一貫性、独立性、永続性)が必要です。
原子性(Atomicity)は、悉無律の操作です。たとえば、1つの口座に対する引落しともう1つの口座に対する振込みで構成されるトランザクションは、両方の操作が正常に実行されないかぎりコミットされません。通常、原子性はデータベース管理システムによって提供されます。
一貫性(Consistency)は、システムの状態を反映します。システムでは、状態不変式に基づいて常に一貫性が保持されます。トランザクションによって、状態の一貫性を確認するコードを記述できます。
独立性(Isolation)は、共有データに対する操作をトランザクション間で認識されないようにします。
永続性(Durability)は、完了したトランザクションの結果は永続的であり、損失されないことを保証します。
EJB仕様には、Enterprise Beanによって操作されるデータに対してトランザクション一貫性を適用するアプリケーションの作成に関する記述があります。ただし、分散トランザクションをサポートする他の仕様とは異なり、EJB仕様では、トランザクションを使用する特殊なコードをEnterprise BeanおよびEJBクライアントの開発者が記述する必要はありません。かわりに、トランザクション管理は、EJBモジュールおよびEnterprise Beanに関連付けられている2つのデプロイメント・ディスクリプタ属性に基づいて、コンテナによって行われます。EJBアプリケーションの開発者は、アプリケーションのビジネス・ロジックの処理に専念できます。
J2EE 1.3準拠のEJBコンテナでは、最も一般的なトランザクションであるフラット・トランザクションがサポートされている必要があります。フラット・トランザクションには、子(ネストした)トランザクションを含めることはできません。EJBでサポートされているトランザクション・タイプは、このトランザクション・タイプのみです。
JTA APIによって、開始トランザクションおよび終了トランザクションが指定されます。
interface javax.transaction.UserTransaction { public abstract void begin(); public abstract void commit(); public abstract void rollback(); public abstract void setRollbackOnly(); public abstract int getStatus(); public abstract void setTransactionTimeout(int); } interface javax.transaction.Status { public static final int STATUS_ACTIVE; public static final int STATUS_MARKED_ROLLBACK; public static final int STATUS_PREPARED; public static final int STATUS_COMMITTED; public static final int STATUS_ROLLEDBACK; public static final int STATUS_UNKNOWN; public static final int STATUS_NO_TRANSACTION; public static final int STATUS_PREPARING; public static final int STATUS_COMMITTING; public static final int STATUS_ROLLING_BACK; }
JTAのUserTransaction
インタフェースは、実際はアプリケーション・サーバーのトランザクション・マネージャに対するインタフェースです。これは、トランザクション・マネージャによって公開されているパブリックAPIです。このインタフェースへの参照を取得するには、JNDIを使用してEJBホームやJDBCドライバなどを参照する場合と同様に、JNDIを使用してインタフェースを参照する必要があります。アプリケーション・サーバーでは、JTAをjava:comp/UserTransaction
下に公開する必要があります。
Context ctx = new InitialContext(...); javax.transaction.UserTransaction userTran = (javax.transaction.UserTransaction) PortableRemoteObject.narrow( ctx.lookup(Òjavax.transaction.UserTransactionÓ), javax.transaction.UserTransaction.class);
トランザクション境界によって、トランザクションの開始および終了がマークされます。アプリケーション開発者が境界を選択します。J2EE仕様には、トランザクション境界を制御する方法として、Beanコードの内部からプログラムで行う方法(Bean管理のトランザクション)、クライアント・コードからプログラムで行う方法(クライアント管理のトランザクション)およびデプロイメント・ディスクリプタの内部で宣言して行う方法(コンテナ管理のトランザクション)の3つが記述されています。
Javaクライアントでは、javax.transaction.UserTransaction
インタフェースを使用してトランザクション境界を明示的に指定できます。クライアント・プログラムでjavax.transaction.UserTransaction
インタフェースを取得するには、JNDI APIを使用します。EJB仕様に、すべてのJavaクライアントでjavax.transaction.UserTransaction
を使用できることは示されていません。J2EE仕様には、javax.transaction.UserTransaction
インタフェースを使用できるクライアント環境が指定されています。
クライアントでEnterprise Beanを起動すると、常にコンテナが介入してメソッドを起動します。この介入によって、コンテナで、デプロイメント・ディスクリプタに設定されているトランザクション属性を宣言として使用してトランザクション境界を制御できます。
たとえば、Enterprise Beanメソッドが「必須」トランザクション属性で構成されている場合、コンテナの動作は次のようになります。クライアント・リクエストがトランザクション・コンテキストに関連付けられていない場合、トランザクション・コンテキストが必要なEnterprise Beanメソッドがクライアントによって起動されると、常にコンテナによってトランザクションが自動的に開始されます。クライアント・リクエストにトランザクション・コンテキストが含まれている場合は、コンテナによってクライアント・トランザクションにEnterprise Beanメソッドが含められます。
エンティティBeanは、コンテナ管理のトランザクション境界を使用して設計する必要があります。コンテナ管理の永続性を使用するエンティティBeanの場合、トランザクションの独立性は、コンテナ・プロバイダのツールによって生成されるデータ・アクセス・クラスによって管理されます。このツールを使用する場合は、データ・アクセス・クラスによって実行される分離レベルの管理が、トランザクション内のリソース・マネージャに対する分離レベルのリクエストと競合しないようにする必要があります。
Bean管理のトランザクション境界が使用されているEnterprise Beanは、セッションBeanである必要があります。トランザクションを開始するインスタンスでは、そのトランザクションが終了してから別の新しいトランザクションを開始する必要があります。セッションBeanでは、EJBContext
およびjavax.transaction.UserTransaction
オブジェクトを使用して、トランザクション境界をプログラムで指定することができます。Bean管理のトランザクション境界が使用されているセッションBeanの場合、リソース・マネージャに固有のAPIを使用して、Beanコードのプログラムで分離レベルをEnterprise Beanのメソッドに指定できます。たとえば、Beanプロバイダでは、java.sql.Connection.setTransactionIsolation(...)
メソッドを使用してデータベース・アクセスに適切な分離レベルを設定できます。
トランザクションに対して、セッションBeanでは、コンテナ管理のトランザクションまたはBean管理のトランザクションのいずれかを使用できます。エンティティBeanでは、コンテナ管理のトランザクションを使用する必要があります。Bean管理またはコンテナ管理のいずれのトランザクション境界がEnterprise Beanで使用されている場合でも、EJBコンテナおよびサーバー・プロバイダによってトランザクション管理が実装されます。
トランザクションの独立性属性によって、コンテナでデータベース内の同時読取りを制限する方法が指定されます。EJB 1.1仕様では、コンテナ管理のトランザクション境界が使用されているBeanでトランザクションの分離レベルを管理する場合のガイドラインが削除されました。ただし、BeanデプロイヤでEJBの同時実行性を制御するメカニズムが必要なため、JBossでは、次の項で説明する他のメカニズムとともに、同時実行性を制御するメカニズムが継続してサポートされます。
CMPを使用すると、コンテナ/Beanレベルでのコミット時/即時ロック(同時実行性)ではなく、データベース・レベルでの読取り/書込みロックの設定を試行する場合、データベースによって異なるSQL文が必要となります。 たとえば、内容が保証されない読取り/アンリピータブル・リード/仮読取りを回避するには、ロック(トランザクションの分離レベルでは「Serializable
」)を設定するメソッドとして、MS-SQL ServerではSELECT ... AT ISLOLATION SERIALIZABLE
、OracleではSELECT ... FOR UPDATE
が必要となります。したがって、ベンダー固有の句を使用せずに、一般的なSQL句をデータベース・レベルのトランザクションおよびロックと組み合せて使用することは困難です。
EJB 1.1では、単純なタイムスタンプ/バージョニングのメカニズムが必要となります(データベース・レベルの読取り/書込みロックの設定は、EJB 2.0でもEJBベンダーの責務となっているようですが、ベンダーによって提供される場合も提供されない場合もあります)。タイムスタンプおよびバージョニングでは、クライアントが変更用のデータを送信した場合にバージョンの比較のみが行われます。読取りは、異なるトランザクションの様々なクライアントによって行われた可能性があります。別のクライアントがエンティティBeanインスタンス内の同じデータを更新しようとしたときに、そのデータが別のクライアントによって更新されていた場合は、バージョン番号が一致しないため、クライアントに情報をリフレッシュ(データを再度取得して、クライアントが初めて変更用のデータをリクエストした後に変更された内容を確認)するように効率的に指示する例外を発生させることができます。これは、Update
<table>
set
<fields>
where
<fields>
=
<fields_read_at_transaction_start>
を実行することに似ています。唯一異なる点として、前述の方法はトランザクションにまたがって適用される(つまり、クライアントは、別のクライアントによってコミットされた変更を上書きできない)という点があげられます。
Bean管理のトランザクションを使用するセッションBeanでは、Beanの各メソッドにトランザクション属性が関連付けられています。この属性値によって、Beanに関連するトランザクションを管理する方法がコンテナに指示されます。Beanには、各メソッドに関連付けることができる6つの異なるトランザクション属性があります。この関連付けは、デプロイ時にアプリケーション・アセンブラまたはデプロイヤによって行われます。
EJBでは、分散フラット・トランザクションがサポートされています。分散メカニズムによって、複数のEJBサーバー上のBeanオブジェクトを処理対象としたり、1つのトランザクションで複数のデータベースのデータを更新することができます。クライアントによってBeanに対して起動されるすべてのメソッドは、Beanのコンテナによって監視されるため、対応するBeanのデプロイメント・ディスクリプタに指定されているトランザクション属性に従ってトランザクションを管理できます。
特定のトランザクション属性は、Bean全体に関連付けてすべてのメソッドに適用するか、または個々のメソッドにのみ適用することができます。トランザクションの有効範囲は、関連するBeanオブジェクトによって共有されるトランザクション・コンテキストで定義されます。
EJBコンテナでは、エンティティBeanのスマート・キャッシュを使用できるため、一部の操作をデータベース・レベルではなくメモリー内で実行できます。キャッシュを使用すると、変更されていないデータへのデータベース・アクセスが行われなくなるため、データベース接続に使用されるシステム・リソースが節約されます。トランザクションのコミット時にコンテナで使用可能なキャッシュ・オプションは、次の3つです。
オプションA: コンテナによって、すぐに使用できるインスタンスがトランザクション間でキャッシュされます。このインスタンスによって、永続記憶域内のオブジェクトの状態に明示的にアクセスできます。つまり、EJBの各インスタンスはメモリー内に保持されます。このオプションは、JBossおよびOC4Jでサポートされていますが、単一ノード・システムでのみ使用する必要があります。この制限は、JBossおよびOC4Jのいずれによっても適用されません。したがって、Beanデプロイヤがこの制限を適用する必要があります。これは、このオプションを使用しているBeanは、単一のコンテナ内でのみ使用されることを意味します。したがって、Beanのすべてのクライアントは、常に特定のコンテナ内の1つのBeanインスタンスにリクエストを送信する必要があります。
オプションB: コンテナによって、永続的なオブジェクト状態にアクセスできないインスタンスがトランザクション間でキャッシュされます。このオプションは、JBossおよびOC4Jではサポートされていません。
オプションC: コンテナによって、トランザクション間でのインスタンスのキャッシュは行われません。エンティティBeanの状態は、前回読み取られたときと値が変わっていない場合でも、各トランザクションの開始時に1回読み取られます。インスタンスは、トランザクションの完了後、プールに戻されます。これは、JBossおよびOC4Jでサポートされているデフォルトのオプションであり、複数ノード構成ではこのオプションを使用する必要があります。
JBossでは、EJB 1.0仕様および次の機能がサポートされています。