前回は簡単なEJBコンポーネントの作成方法と@EJBというアノテーション(注釈)を使ったDI(Dependency Injection:依存性の注入)について説明しました。今回は,EJBを使ったアプリケーションにおいて利用頻度の高い「セッションBean」と「宣言的トランザクション」について解説します。

 セッションBeanは,主に業務ロジックや業務フローを実装するEJBのコンポーネントです。通常のJavaクラスに@Statelessや@Statefulなどのアノテーションを付けることで作成できます。

 またEJBでは,トランザクションの管理をコンテナに任せることで宣言的トランザクションを利用できます。宣言的トランザクションとは「トランザクションの開始や終了,トランザクションの振る舞いをプログラム・コードとして明示的に記述するのではなく,アノテーションやXMLファイルなどを利用してプログラム・コード外で制御すること」をいいます。宣言的トランザクションを利用することで,トランザクションを制御する煩雑なコーディングが不要になるため,開発者の負担が少なくなります。

 本稿では,宣言的トランザクションを使ってデータベースを更新するセッションBeanのサンプルを作成し,両者の特徴を紹介します。

EJB 3.0で定義されている3種類のオブジェクト

 EJB 3.0には,セッションBeanを含めて3種類のオブジェクトが定義されています。セッションBean,メッセージ・ドリブンBean,エンティティです。これらのオブジェクトには以下の特徴があります。

セッションBean

 呼び出し元のプログラムに呼び出されると同期的に実行されます。ステートフル・セッションBeanとステートレス・セッションBeanの2種類があります(詳細は後述)。ステートレス・セッションBeanはWebサービスとして実行することができます。

メッセージ・ドリブンBean

 メッセージ・ドリブンBeanは,メッセージを受信すると起動し,非同期的に実行されます。そのため,呼び出し元のプログラム(メッセージを送信するプログラム)は,メッセージを送ってしまえばメッセージ・ドリブンBeanの実行の完了を待つことなく次の処理に進むことができます。

エンティティ

 データベースのデータをオブジェクトとして表したものです。典型的なケースでは,テーブルのデータ1件が一つのエンティティのインスタンスになります。「Java Persistence API」という新しいAPIを使ってデータ・アクセスを行う際に利用します。

 主に業務ロジックや業務フローを実装するクラスとして作成されるのが,セッションBeanとメッセージ・ドリブンBeanです。あるメソッドを呼び出し,そのメソッドが完了してから次の処理へ進むといった同期的なロジックを実行する場合には,ロジックをセッションBeanとして実装します。ロジックを非同期で実行したい場合には,メッセージ・ドリブンBeanとして実装します。

状態を維持するかどうかで2種類あるセッションBean

 セッションBeanは,メソッドの呼び出しにまたがってステート(状態)を維持するステートフル・セッションBeanと,維持しないステートレス・セッションBeanに分けられます。「ステート」とは呼び出し元のプログラムとセッションBeanの「対話処理の状態」を意味しています。

 対話処理の状態とはどのようなものでしょうか。ステートフル・セッションBeanとステートレス・セッションBeanの典型的な使い方を通して説明しましょう(図1)。

図1 ステートフル・セッションBeanとステートレス・セッションBeanの違い [画像のクリックで拡大表示]

 まず対話処理の状態を維持する場合です。ショッピングカートを表すShoppingCartBeanが,ステートフル・セッションBeanとして実装されているとします。ShoppingCartBeanは製品をカートに入れることを示すbuyメソッドと製品の支払いを実行するcheckoutメソッドの二つのメソッドを持っています。呼び出し元のプログラムはbuyメソッドを複数回実行して製品をカートに入れ,最後にcheckoutメソッドで支払いを実行します。checkoutメソッドでは,カートに入れた購入情報をデータベースに書き出します。

 このシナリオでは,ShoppingCartBeanはcheckoutメソッドが呼び出されるまでbuyメソッドでカートに入れられた製品を覚えておく必要があります。このような場合,ステートフル・セッションBeanはインスタンス・フィールドを利用して対話処理の状態を維持します。つまり,ShoppingCartBeanは「buyメソッドの実行により自身のフィールド(例えばList型やMap型のフィールド)に製品を格納し,checkoutメソッドの実行でそのフィールドからデータを取り出す」というようにメソッド間にまたがって状態を維持するのです。

 一方,対話処理の状態を維持しない場合には,ステートレス・セッションBeanを使います。ステートレス・セッションBeanのそれぞれのメソッドは「パラメータを受け取り,処理し,結果を返す」という一つのメソッドで完結した動作を行います。メソッド呼び出しにまたがった状態は維持しません。図1では,受注エントリを示すOrderEntryBeanがステートレス・セッションBeanとして実装されているものとします。OrderEntryBeanは注文を行うorderメソッドと注文をキャンセルするcancelメソッドを持ちますが,どちらのメソッドを実行してもOrderEntryBeanの内部状態は変わりません。このため,どちらかのメソッドの実行が他方に影響を与えるということがありません。

 一見すると,対話処理の状態を維持するステートフル・セッションBeanのほうが便利に思えるかもしれません。しかし,実はステートレス・セッションBeanの方が扱いやすいのです。理由は二つあります。

理由1:パフォーマンスの問題

 ステートフル・セッションBeanは呼び出しプログラムごとに対話処理の状態を維持します。このため,ステートフル・セッションBeanに多量にアクセスするとリソースを圧迫します。そこでEJBコンテナはリソースを節約するために,メソッドが実行されていないステートフル・セッションBeanをハードディスクなどのストレージに書き出し(非活性化),必要となる時点で読み込みます(活性化)。非活性化や活性化が多量に発生すると,パフォーマンスが問題になってきます。

 一方,ステートレス・セッションBeanにはこのような問題はありません。対話処理の状態を維持する必要がなく,非活性化や活性化が不要だからです。

理由2:設計とテストのしやすさ

 ステートレス・セッションBeanは非活性化や活性化がないぶん,ステートフル・セッションBeanよりもライフサイクルがシンプルです。このため,設計における考慮事項が少なくて済みます。

 ステートフル・セッションBeanのメソッドの実行結果はパラメータの値と対話処理の状態の両方に依存しますが,ステートレス・セッションBeanのメソッドの実行結果はパラメータにしか依存しません。ステートレス・セッションBeanのほうがメソッドの実行結果のパターンが少なく処理内容を把握しやすいため,設計とテストが行いやすくなります。

 対話処理の状態の維持にはステートフル・セッションBeanが必須というわけではありません。Webアプリケーションなら,状態の維持にHttpSessionを使い,Webコンポーネントから必要なデータをパラメータで渡してステートレス・セッションBeanを呼び出すことができます。これによりステートフル・セッションBeanを使わずに状態を維持できます。要件によりますが,セッションBeanを利用する場合,まずステートレス・セッションBeanの利用を検討するのがお勧めです。