WebSphere Application Server で Spring および OpenJPA を使用する

この記事では、Spring Framework と Apache OpenJPA を利用して、J2EE プロジェクトの作業効率とアプリケーション開発を改善する方法を説明します。サンプル・アプリケーションでは Spring を Web アプリケーションの異なるアーキテクチャー・レイヤー内のいくつかのシナリオで使用しますが、特にサービス・レイヤーやデータ・アクセス・レイヤーなどのビジネス層に焦点を当てます。

Marko Asplund, Senior Consultant, Ixonos oyj

author photoMarko Asplund は、Java テクノロジーで実装されることの多い、エンタープライズ情報システムの設計および実装を専門とするシニア・コンサルタントです。Sun Certified Enterprise Architect、Sun Certified Business Components Developer、Sun Certified Java Programmer の資格を取得しています。



2008年 10月 29日

はじめに

Spring は J2EE 開発を単純化するのに役立つ Java™ フレームワークです。Spring には J2EE アプリケーションのあらゆるレイヤー向けの機能が装備されています。また、Spring は特定のプログラミング・モデルを使用することを強制しないため、ランタイム環境に拘束されません。言い換えると、Spring は Java SE 環境以外のアプリケーション・サーバーで使用できるということです。近年の Spring 人気には、このような設計方針が少なくとも一部貢献していると思われます。Spring を支持する人たちの中には、Spring Framework は J2EE に取って代わるものと考えている人もあるようですが、著者は、J2EE を使用すれば Spring を使わなくてよい、あるいは Spring を使用すれば J2EE を使わなくてよいというのではなく、両者は相互にうまく補完するものと考えています。

OpenJPA は SolarMetric Kodo Java Data Objects (JDO) 実装の流れを汲む、Java Persistence API (JPA) の実装です。Kodo は BEA に買収された後拡張されて Java Persistence API を実装するようになり、最終的に Apache OpenJPA としてそのコード・ベースはオープン・ソース化されました。現在、OpenJPA は特に BEA と IBM の貢献によって、実務に耐える Java 用のオブジェクト/リレーショナル・マッピング・ツールとなりました。


サンプル・アプリケーション

予備知識 WebSphere Application Server V6.1 および V7 は EJB 3 をサポートします。EJB 3 は JPA をネイティブでサポートし、Spring と同様に多くの面で開発を単純化します。詳細については、この記事およびこの記事の最後にある「参考文献」を参照してください。

この記事では、「Events」という名前の基本的な Web アプリケーションを使って、Spring と OpenJPA を活用して WebSphere Application Server 上で稼働するアプリケーションを開発するためのさまざまな手法を紹介します。これらの手法を組み合わせて使用する方法を分かりやすく説明するために、簡単なアプリケーションを設計しました。サンプルとして、イベントの追加、イベントのリスト、イベントの編集というシンプルなユース・ケースが 3 つ用意されています。ユーザーによる情報入力はリレーショナル・データベースに永続的に保存されます。システム内に格納されたイベントのリストは図 1 のように表示できます。イベント・リストには、イベントごとに内部識別子とイベントのタイトルが表示されます。

図 1. イベントのリスト
イベントのリスト

リスト・ビューの「Add」ボタンをクリックしてイベントを追加できます。イベントを追加するには、「Title」、「Category」、「Start Time」、「Duration」の各フィールドに入力する必要があります。必須フィールドにはアスタリスクが付けられています (図 2)。イベントを追加するときは、ID フィールドは非アクティブになります。

また、リスト・ビューにある「Edit」リンクを使用すると、対応するイベント・データを変更できます。

図 2. イベントの追加
イベントの追加

アーキテクチャーの概要

Events アプリケーションは Java SE 5 と J2EE 1.4 を使用しており、WebSphere Application Server 6.1 上で稼働します。また、ユーザー・インターフェースの実装時には JavaServer Faces (JSF) 1.1 テクノロジーを活用します。今回、著者は Spring Framework をアプリケーションのアーキテクチャー全体で、また Java Persistence API をデータ・アクセス・レイヤーで使用しました。

図 3 にこのアプリケーションの簡単な構成を示します。JSF ページでは、JSF Managed Bean を使用してイベント情報の読み込みや格納といったアクションを実行します。再利用可能性を向上させるために、アクションを実行する Bean (EventBacking) と、状態を保持する Bean (EventSession) の 2 つの Managed Bean が用意されています。アクション (または「バッキング」) Bean はリクエスト・スコープですが、状態保持 Bean はセッション・スコープです。このように分けることで、アプリケーション内にビューが多数存在する場合でも、異なるビュー間で両方の種類の Bean を再利用しやすくなります。現在のユーザー・セッションの状態は JSF Managed Bean 機能を介してバッキング Bean に注入されるため、バッキング Bean はその状態へのハンドルを取得します。

図 3. アーキテクチャー図
アーキテクチャー図

バッキング Bean はイベント追加リクエストの処理を EventServiceEJB ステートレス・セッション Bean に、イベントのリストおよび保存処理を EventService 実装に委任します。 普通、データベース・データには Web または EJB のいずれかのアプリケーション層でアクセスしますが、両方は使用しません。サンプル・アプリケーションでは、サービス・レイヤーを通じて両方の層からデータベースにアクセスし、これら 2 つで Spring をどう使うのかを確認します。

EventServiceEJB は処理をさらに EventService インターフェース実装クラスに委任します。このサービス実装クラスは、データ・アクセス・オブジェクト (DAO) の実装を使用して、永続的なデータ・ストレージとやり取りします。サービス・レイヤーには特定のイベントの検索、イベントの作成、更新、削除するメソッドと、さらにすべてのイベントをリストするメソッドがあります。DAO は Java Persistence API の EntityManager を使用してデータベースのデータにアクセスします (図 4 を参照)。

図 4. サービス・レイヤーの図
サービス・レイヤーの図

このアーキテクチャーには複数のレイヤーが含まれており、その中のサービス・レイヤーの主な目的は、アーキテクチャーに実際のアプリケーションを真似させることです。後からお見せしますが、EventService サービス・レイヤーが果たす具体的な役割の 1 つは、トランザクション管理のプラグイン・ポイントとして機能することです。

アプリケーションのドメイン・モデル (図 5) は、JPA エンティティーとして実装された Event という名前の 1 つの Java クラスで構成されています。著者はこのエンティティーを、対応する列を含む 1 つのデータベースの表にマップしました。

図 5. ドメイン・モデル
ドメイン・モデル

アプリケーションは次の 4 つのプロジェクトで構成されています。

  • events-ear: EAR パッケージング、共有ライブラリーなど
  • events-ejb: ビジネス・ロジック層、EJB セッション Bean
  • events-service: サービス・レイヤーおよびデータ・アクセス
  • events-war: Web 層

開発

IDE を使用してサンプル・アプリケーションを操作するには、以下のソフトウェアをあらかじめインストールしておく必要があります。

  • Eclipse 3.4 for Java EE
  • IBM WebSphere Application Server v6.1 (v6.1.0.9 以降 6.1 リリース。 v6.1.0.17 でテスト済みのサンプル・アプリケーション)
  • Java SE JDK 1.5 (WebSphere Application Server とバンドルされた JDK でも可)
  • IBM DB2 (DB2 UDB 8.2 または Apache Derby v10.4.2.0)

サンプル・アプリケーションでは Spring Framework v2.5.5 と Apache OpenJPA v1.2.0 を使用しています (使用されているその他の API およびバージョンについては、 events-ear/docs/libraries.txt ファイルを参照)。

アプリケーションのプロジェクトを以下のようにセットアップしてください。

  1. events.zip パッケージをダウンロードし、解凍します。
  2. ソース・ツリーを Eclipse にインポートします。「File」メニューの「Import」を選択し、「General / Existing Projects into Workspace」を選択します。インポートのルートとして、解凍したソース・ツリーのルート・ディレクトリーを選択します。「Import」ダイアログが図 6 のように表示されます。
    図 6. プロジェクトのインポート
    プロジェクトのインポート
  3. WAS 6.1 J2EE」という名前のユーザー・ライブラリーを作成します。「Window - Preferences」を選択し、「Java / Build Path / User Libraries」に移動して「New」をクリックし、新しいライブラリーを作成します。ライブラリーが自動的に events-ejb および events-war のプロジェクト・ビルド・パスに追加されるように、「WAS 6.1 J2EE」という名前を使用することが重要です (図 7 を参照)。ライブラリーを作成した後、インストールされている WAS 6.1 から j2ee.jar ファイルをライブラリーに追加します。このファイルは ${app_server_root}/lib/j2ee.jar にあります (app_server_root は Application Server 6.1 のインストール・ルート・ディレクトリーのことです)。
    図 7. ユーザー・ライブラリーの作成
    ユーザー・ライブラリーの作成
  4. 必要なクラス・ライブラリーをダウンロードし、events-ear/docs/libraries.txt の記述に従って、プロジェクト・ツリー内の適切な場所に配置します。
  5. events-ear ディレクトリーの build.properties ファイルを編集します。使用する Application Server 6.1 インストール・パスを反映するために、was-profile.root の変数値を設定する必要があります。
  6. プロジェクト EAR ファイルをビルドします。events-ear にある build.xml Ant ビルド・ファイルを開きます。Eclipse の「Outline」ビューで「dist」ターゲットを右クリックし、「Run As / Ant Build」を選択します。ビルドが完了すると、ソース・ツリーのルートにある dist ディレクトリーに EAR ファイルが配置されます。

デプロイメント

アプリケーションの EAR パッケージのビルドが完了したら、以下の手順に従って events.ear をアプリケーション・サーバーにデプロイしてください。EAR パッケージはプロジェクト構造のルートの下の dist ディレクトリーにあります。

  1. アプリケーション用のデータベース・スキーマを作成するか、既存のスキーマを選択します。
  2. events.ddl の DDL ステートメントを実行し、データベース表を (events-service/setup 内に) 作成します 。
  3. WAS コンソールを開き、上で作成したデータベース・スキーマに接続する XA データ・ソースをセットアップします。図 8 に示すように、データ・ソースには jdbc/eventDS という JNDI 名を使用します。
    図 8. Bean の JNDI 名の指定
    Bean の JNDI 名の指定
  4. アプリケーションをデプロイします。Application Server コンソールで Applications / Enterprise Applications に移動し、「Install」をクリックします。デプロイメント・ウィザードが起動します。新しいアプリケーションへのパスの入力を求められたら、events.ear ファイルへのパスを選択します。
  5. 図9 に示すように、ejb/EventServiceEJB のリソース参照を ejb/EventServiceEJB にマップします。
    図 9. EJB 参照の Bean へのマッピング
    EJB 参照の Bean へのマッピング
  6. 次に、jdbc/eventDS リソース参照を jdbc/eventDS JNDI 名にマップします (図 10)。
    図 10. リソース参照のリソースへのマッピング
    リソース参照のリソースへのマッピング
  7. 最後に、デプロイメント・ウィザードが完了したら、「Manage Applications」をクリックし、events-ear / Manage Modules / events-war を選択します。クラス・ローダーの順序を「Classes loaded with application class loader first」に設定し、「OK」、「Save」を順にクリックします。
    図 11. events-war モジュールの管理
    events-war モジュールの管理
  8. Enterprise Applications リストからアプリケーションを起動します。アプリケーションが起動すると、「Application Status」列に緑色の矢印アイコンが表示されます。
  9. ブラウザーで http://localhost:9080/events-war/faces/jsp/eventsList.jspx をポイントして、アプリケーションにアクセスします。この URL は、使用している Application Server の URL に合わせる必要があります。

Spring Framework と OpenJPA の使用

ここまでは、ユース・ケース、開発、デプロイメントの観点からサンプル・アプリケーションを取り上げてきました。今度は、ブートストラッピングと Spring および OpenJPA の使用方法に注目しましょう。次のセクションでは、企業の Java 開発者の作業効率を高めるために生まれた、疎結合、トランザクション管理、例外処理、データ・アクセス、データ配布といった Spring の機能について、その使用方法をいくつか説明します。

コンテナーのインスタンス化

Spring Framework の基本的な方針の 1 つは、サービス・オブジェクト (Spring の語法に従った Bean) とその相互依存関係を軽量コンテナー内部でフレームワークによって管理させることを開発者が宣言できるという点です。宣言された Bean とその依存関係のライフ・サイクル管理はコンテナーが担当します。コンテナーがインスタンス化されると、Spring は宣言されているコラボレーション・オブジェクトをオートワイヤリングします。依存オブジェクトがコラボレーターを自分でルックアップしなければならないのとは異なり、依存オブジェクトのコラボレーターへのアクセスを確保するのはフレームワークの役目であるため、Spring は制御の反転 (IoC: Inversion of Control) コンテナーとも呼ばれています。Bean は、 XML 構成ファイルを始めとして、さまざまな方法を使って宣言できます。プログラム的に、あるいはアノテーション・ベースで Bean を宣言することもできます。

コンテナーはアプリケーション層に応じて多少異なる方法でインスタンス化します。Web 層では、リスト 1 の XML フラグメントを自分の /WEB-INF/web.xml ファイルに挿入するだけでコンテナーをインスタンス化できます。

リスト 1. リスナー・クラス
<listener>
  <listener-class>
    org.springframework.web.context.ContextLoaderListener
  </listener-class>
</listener>

デフォルトでは、このクラスは Spring の Bean の宣言が含まれるはずの /WEB-INF/applicationContext.xml ファイルをロードします。必要に応じて、デフォルトの構成ファイルのパスをカスタマイズできます。Web アプリケーションの ServletContext は、コンテナー・インスタンスのバインド対象として使用されます。こうすることにより、これ以後の使用時に繰り返しインスタンス化する必要がなくなります。

EJB には J2EE 1.4 の Web 層初期化メカニズムのようなアプリケーション初期化の標準的な方法がないため、ここでは Spring コンテナーを多少異なる方法でインスタンス化する必要があります。Spring には、コンテナーを作成したりロードするために、いくつかの異なる実装クラスが含まれています。コンテナーのインスタンス化は高コストであるため、インスタンスが必要になるたびにインスタンス化を実行するのは避けねばなりません。EJB 仕様には、コンテナー・インスタンスを共有するのに適した機能がないため、singleton ベースのインスタンス化戦略を用いるのが適していることがしばしばあります。

この方法を使うには、通常は beanRefContext.xml (デフォルト) という名前の EJB 固有の Spring のブートストラップ構成ファイルを作成し、それによって他の Bean 構成ファイルをロードする必要があります。また、EJB 実装クラスで setSessionContext をオーバーライドします。EJB 層でのコンテナーのインスタンス化は、Web 層でのインスタンス化のように非侵入的には機能しません。明らかな欠点として、Spring API を使用して明示的に Bean を lookup しなければならないことが挙げられます。

これと似たアプローチが、Spring のEJB 実装をサポートする抽象クラスの 1 つを、EJB 実装の基底クラスに使用するやり方です。これらの便利なクラスは、コーディングの際に EJB コンポーネント・インターフェース・メソッドの実装を不要にするだけでなく、Spring コンテナーのインスタンス化にも対応します。ただし、それでも beanRefContext.xml を作成し、setSessionContext を実装する必要はあります。この方法の 1 つの弱点は、自分の EJB 基底クラスが使えないことです。

Web 層ででも ServletContext が利用できないという事態に陥る場合がときどきあります。この事態は、サード・パーティー製のアプリケーションやフレームワークを拡張中で、コード内で Spring の Bean を使用することを希望しているが、API が拡張中のクラスにコンテキストを渡さないときに起きる可能性があります。この場合は上で説明した EJB 層の場合と同じく、Spring コンテナーをインスタンス化して対処できます。

依存性注入

Spring では、コンテナーが依存性注入 (DI: Dependency Injection) という手法を使って、依存オブジェクトがコラボレーション・オブジェクトへの参照を利用できるようにします。依存性注入はコラボレーション・オブジェクトへのアクセスを提供する J2EE の標準のメカニズムとは異なります。J2EE では、JNDI Environment Naming Context (ENC) というメカニズムを使って、名前空間経由でコラボレーション・オブジェクトを利用可能にするとともに、それらへの参照を取得します。依存オブジェクトは、JNDI ENC を使用することによって、特定のコラボレーターに対する参照を明示的に要求できます。

Spring の DI を使用する場合、プログラマーはコンストラクター注入か setter 注入のいずれかのバリアントを使用し、依存オブジェクトがコラボレーターの参照を利用できるようにして、コンテナーに依存性を解決させます。コンストラクター注入では、コンテナーはコンストラクターの呼び出しでコラボレーターを渡します。setter 注入では、コンテナーは依存オブジェクトを作成した後、mutator メソッドの呼び出しを使用して参照を渡します。どちらの場合も Spring の構成ファイルを使用するなどして依存関係を宣言し、対応するコンストラクターや mutator メソッドを依存オブジェクト・クラスに追加しなければなりません。

デザイン・パターンに関する古典、「Design Patterns: Elements of Reusable Object-Oriented Software (オブジェクト指向における再利用のためのデザイン・パターン)」では、「実装ではなく、インターフェースに重点を置いてプログラムする」という設計方針が推進されています。しかし、たとえインターフェースに重点を置いてプログラムしても、実装クラスをどこかでインスタンス化する必要があります。プログラムを使えばインスタンス化は可能ですが、それによってそのコードは具象実装クラスに依存することになります。実装クラスをインスタンス化するためのファクトリー・クラスを作成するという別のアプローチもありますが、それでもコードには実装クラスへの依存関係が残ります。依存関係を制限し、その数を減らすことができたとしても、ソース・コードのレベルでは、依存関係は存在するということに留意しておくことが重要です。

リスト 2 の EventService インターフェース実装クラスは、このアプローチを表したものです。ここでは、サービス実装は、DAO 実装クラスではなく、EventDAO インターフェースのみとソース・レベルの依存関係を持っています。

リスト 2. EventService インターフェース実装
public class EventServiceImpl implements EventService {
  private EventDAO eventDAO;

  public void setEventDAO(EventDAO eventDAO) {
    this.eventDAO = eventDAO;
  }
  // …
}

依存関係は Spring 構成で次のように宣言されます。

<bean id="eventDAO" class="events.service.EventDAOJPA"/>

<bean id="eventService" class="events.service.EventServiceImpl">
  <property name="eventDAO" ref="eventDAO"/>
</bean>

Spring では、依存関係を構成ファイルで宣言することにより疎結合が提供され、次にコラボレーターが依存オブジェクトにワイヤリングされます。これによって、呼び出し元を実装クラスから完全に分離できるため、コードの柔軟性が高まります。こうすることで、実装クラスの切り替えは Spring 構成ファイルを変更するだけで済みます。

例外処理

近年、Java API による Java 例外モデルの使用方法に関して批判が出ており、もともと発生する確率が極めて低く、しかも合理的に回復させるのが難しいシステムやプログラマーの問題が原因で起きるフォールト状態の処理をプログラマーに押し付けるべきではなく、そのような状態にはチェックなし例外を使用して、そういった状態をオプションで処理できるようにすべきである、という声が多く上がってきています。このような人たちは、通常の運用時に発生する可能性のあるアプリケーションやユーザーのエラーだけをチェック例外を使ってレポートすべきであると考えています。このような例外処理戦略は一般に広く受け入れられるようになっており、Spring を始めとする多くのフレームワークや API がこの考え方に従っています。

サービス・レイヤーのサポート

うまく設計された実装済みのサービス・レイヤーがあれば、アプリケーションの拡張性と頑強性に大きな効果が期待できます。再利用性に優れたサービス・レイヤーがあると、新しいエンド・ユーザー機能の追加や既存機能の変更を大幅に簡素化できます。

トランザクション区分の実装に不備があると、サービス・レイヤーの再利用性が損なわれる恐れがあります。これは大きな問題です。サービス・レイヤーは多彩なユース・ケースの実装に使われるため、サービス・レイヤーの呼び出し元は多様なコンテキストで操作を行う可能性があります。つまり、呼び出し元には多様なトランザクション要件があるため、サービス・レイヤーは呼び出し元がトランザクション処理を必要に応じて調整することを可能にしておく必要があるのです。

アプリケーションのトランザクション要件がさほど複雑でないとしても、プログラムによるトランザクション区分は手間がかかり、間違いが起きやすい作業です。宣言によるトランザクション区分により、プログラマーはトランザクションの動作に対するソフトウェアのルールを宣言し、トランザクション・マネージャーにこれらのルールを自動的に実行させることができます。Spring はプログラムと宣言の両方のトランザクション区分をサポートします。サンプル・アプリケーションでは、リスト 3 に示す宣言を使用してサービス・レイヤーのトランザクション属性を定義しています。

リスト 3. トランザクション属性
<tx:advice id="txAdvice" transaction-manager="transactionManager">
  <tx:attributes>
    <tx:method name="*" propagation="REQUIRED"/>
  </tx:attributes>
</tx:advice>

<aop:config>
<aop:pointcut id="serviceMethods"
       expression="execution(* events.service.EventService.*(..))"/>
  <aop:advisor advice-ref="txAdvice" pointcut-ref="serviceMethods"/>
</aop:config>

この構成は、Spring のアスペクト指向プログラミング (AOP: Aspect Oriented Programming) に対するサポートを利用しています。リスト 3では、トランザクション・アドバイザー・オブジェクトというオブジェクトを定義し、トランザクション・マネージャーをこのアドバイザーにバインドしています。トランザクション・アドバイザーには、宣言されているすべてのメソッドに、REQUIRED トランザクション属性を適用するように指示しています。REQUIRED トランザクション属性のセマンティクスは J2EE と同じで、メソッドは常にトランザクション内で実行されます。呼び出し元があるトランザクション・コンテキストで稼働している場合、メソッドは呼び出し元のコンテキストで実行されます。それ以外の場合は、新しいトランザクション・コンテキストが作成されます。

次に、aop:config タグ内で 1 組のメソッド、すなわち、トランザクション型の宣言を適用する events.service.EventService インターフェースの全メソッドが定義されています。トランザクションでアドバイスされるクラスは、特別なコンポーネント・インターフェースを実装する必要はなく、トランザクション属性を Plain Old Java Object (POJO) クラスに対して指定できます。これを実装するために、Spring は元のサービス・オブジェクトをトランザクションのプロキシーでラップします。ただし、サービス・オブジェクトのローカルな呼び出しはトランザクション・プロキシーを経由しないため、常に呼び出し元のトランザクション・コンテキストで発生するので注意が必要です。

Spring はトランザクション・マネージャーのインターフェース実装を使用して、実行時に実際のトランザクション区分を実行します。対象となる環境の能力によって、サービス・レイヤーを異なるトランザクション・マネージャーを使用するように構成できます。例えば、サービス・レイヤーが J2EE アプリケーション・サーバー内で稼働するのであれば、リスト 4 に示すように、アプリケーション・サーバーのトランザクション・マネージャーを使うように Spring に指示できます。

リスト 4. トランザクション・マネージャーの構成
<tx:jta-transaction-manager/>

この構成では、Spring はアプリケーション・サーバーのトランザクション・マネージャーを自動的に選択します。Java SE 環境では、リスト 5 に示すように Spring が JPA API のトランザクション区分機能を使用するように構成することができます。

リスト 5. JPA のトランザクション・マネージャーの宣言
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
  <property name="entityManagerFactory" ref="emFactory"/>
</bean>

この手法によって、コードを変更することなく、EJB と Web の両層で、あるいは Java SE 環境で使用できるようにサービス・レイヤーを実装することができます。また、コンテナー外でのサービス・レイヤーのテストも非常に簡単になります。これによって開発サイクルが大幅にスピードアップします。

サービス・レイヤーの再利用性に悪影響を及ぼす可能性のあるもう 1 つの問題が、例外処理の戦略です。例外はアプリケーション全体で一貫性を持って処理しなければなりません。通常システム・エラーを処理することは求められませんが、必要に応じてこのようなエラーを処理することは可能なはずです。JDBC や JPA などのデータ・アクセス API が例外条件について一般的な情報しか提供しないため、具体的な原因を効果的に特定できないことが少なくありません。サービス・レイヤーが特定のエンタープライズ情報システムに格納されているデータにアクセスするためにデータ・アクセス・レイヤーを使用する場合、サービス・レイヤーは情報システム固有の例外を上位レイヤーに波及させてはいけません。

Spring は一貫性のあるデータ・アクセス例外クラスの階層を定義しているため (図 12)、これをサービス・レイヤーや DAO レイヤーの例外の基礎として利用できます。Spring のデータ・アクセス機能は、データ・アクセス例外を自動的にこの例外クラス階層に変換します。また、必要に応じて具体的な例外を追加して、この階層を独自に拡張することもできます。すでに述べたように、この階層内の例外はチェックなし例外です (この図の拡大図を表示)。

図 12. Spring のデータ・アクセス例外階層 (Spring の参考文献から引用)
Spring のデータ・アクセス例外階層 (Spring の参考文献から引用)

DAO サポート

JPA の重要な概念は、そのパーシスタンス・コンテキストです。パーシスタンス・コンテキストとの対話を通じて、リレーショナル・データベースに対するデータ・アクセスの操作を実行できます。パーシスタンス・コンテキストは、管理対象オブジェクトの状態を、データベースに格納されているエンティティーの状態と同期させる役割を果たします。パーシスタンス・コンテキストには、EntityManager インターフェースを介してアクセスできます。

アプリケーションのランタイム環境がコンテナー管理パーシスタンス・コンテキストをサポートしない場合、アプリケーションがコンテキストのライフ・サイクルを明示的に管理する必要があります。これには多少手間がかかりますが、幸いにも Spring がここで役に立ちます。リスト 6 の行によって、Managed Bean を背後で拡張する Bean の一連のポストプロセッサーを使用して Spring コンテナーが構成されます。

リスト 6. Spring コンテナーの構成
<context:annotation-config/>

このプロセッサーにはいくつかの役割がありますが、そのうちの 1 つが Spring の Bean 内でアノテーションを処理することです。これによって、リスト 7 に示すように、JPA パーシスタンス・コンテキストを DAO 実装クラスなどのJava クラスに注入することが可能になります。

リスト 7. JPA パーシスタンス・コンテキスト
@PersistenceContext
private EntityManager em;

Spring はこのアノテーションを通じて、クラスのインスタンスにトランザクションのパーシスタンス・コンテキストを注入します。Spring では、POJO を使ってコンテナー管理パーシスタンス・コンテキストのシミュレーションができるため、DAO 実装が容易になります。Java EE 5 では、パーシスタンス・コンテキストは EJB などの管理対象オブジェクトにのみ注入でき、POJO には注入できないので注意してください。

リスト 8 の行を Spring の構成ファイルに追加するだけで、データ・アクセス実装クラスのデータ・アクセス例外の変換を指示することもできます。

リスト 8. データ・アクセス例外の変換
<bean class="org.springframework.dao.annotation.
      PersistenceExceptionTranslationPostProcessor"/>

さらに、DAO 実装クラスには @Repository アノテーションの付与が必要です。


JavaServer Faces のサポート

JSF には、JSF の EL 式でトップレベルの変数の解決をカスタマイズする仕組みがあります。Spring には変数リゾルバー実装クラスが含まれており、これを使って Spring の Managed Bean を JSF 式で参照できます。このクラスは、それぞれのトップレベルの変数名について、該当する ID の Bean が Spring コンテナーに存在するかどうかを最初にチェックします。Bean が存在すると、クラスはこの Bean に対する参照を解決します。それ以外の場合は、JSF のデフォルトの変数リゾルバーを調べます。このリゾルバーを使用することにより、Spring の Bean を JSF Managed Bean に注入したり、Spring の Bean を JSF ページの EL 式で参照したりできます。リスト 9 に、faces-config.xml ファイルで変数リゾルバーを構成する方法を示します。

リスト 9. 変数リゾルバーの構成
<variable-resolver>
	org.springframework.web.jsf.DelegatingVariableResolver
</variable-resolver>

EJB Bean へのアクセス

EJB セッション Bean を使用すると、呼び出し側のコードが非常に冗長になる場合があります。リモートのステートレス・セッション Bean として実装されているサービス・メソッドを探し、呼び出すコードは通常、リスト 10 のようになります。

リスト 10. EJB セッション Bean の冗長なコード
try {
  Context ctx = new InitialContext();
  Object homeObj = ctx.lookup("java:comp/env/ejb/EventServiceEJB");
  EventServiceEjbHome eventHome = (EventServiceEjbHome)
    PortableRemoteObject.narrow(homeObj, EventServiceEjbHome.class);
  EventServiceEjb eventService = eventHome.create();
  String msg = eventService.getGreeting("world");
} catch (NamingException e) {
  // handle exception
} catch (CreateException e) {
  // handle exception
} catch (RemoteException e) {
  // handle exception
}

ルックアップ・コードと例外処理が、リスト 10 のコードを冗長にしている主な原因です。これらの原因を取り除くためには ServiceLocator パターンを実装して、サービス・ユーザーがサービス実装クラスへの参照を取得するために呼び出す独立したクラスにルックアップ・コードを移動させるのが一般的です。ServiceLocator は、ルックアップや Bean の作成時にスローされたチェック例外をチェックなし例外に変換することもできます。ただし、EJB を使用する場合は、やはり RemoteException 例外を処理する必要があります。

これについても Spring には優れた解決策が用意されています。Spring 構成でこの EJB Bean をSpring Bean として指定し、それらを Spring の通常の依存性注入の方法を使って、任意の他の Spring Bean のコラボレーターとして注入するのです。

リスト 11. リモートのステートレス・セッション Bean を Spring Bean として宣言する
<jee:remote-slsb id="eventServiceEjb"
 jndi-name="java:comp/env/ejb/EventServiceEJB"
 business-interface="events.service.EventService"
 home-interface="events.ejb.EventServiceEjbHome"/>

ターゲット・フィールドのタイプで EJB ビジネス・インターフェース・タイプが指定されている場合、呼び出し元は EJB を呼び出す際の詳細を気にする必要がありません。Spring が NamingExceptionRemoteException などの例外をキャッチし、それらをチェックなし例外として再スローするので、例外処理の手間が省けます。Spring は実際の EJB のリモート・インターフェースのスタブではなく、プロキシー・オブジェクトへの参照を注入することで、ビジネス・メソッドの呼び出し中にスローされた例外もキャッチします。このようにしてプロキシーは EJB への呼び出しをインターセプトし、例外を適切に変換できます。リモート・メソッドの呼び出しでは引き続いて値渡しによる呼び出しのセマンティクスを使用できることを知っておくことが大切です。


結論

Spring によって J2EE プログラミングで従来あった数々の問題を単純化して、プログラミングの生産性を向上させることができます。非侵入的な設計であるため、既存のコード・ベースや新規のアプリケーションにも簡単に導入できます。また、デプロイする機能を取捨選択できます。全スタックの使用は必須ではなく、必要な部分のみを使用できます。Spring は WebSphere Application Server や OpenJPA とも問題なく統合できます。この記事では、Spring の機能の使用方法のごく一部について紹介しました。そのため、各自のプロジェクトに役立ちそうなその他の機能について、以下に示す参考文献を手始めに調べることをお勧めします。


ダウンロード

内容ファイル名サイズ
Sample Web applicationevents.zip37KB

参考文献

学ぶために

製品や技術を入手するために

コメント

developerWorks: サイン・イン

必須フィールドは(*)で示されます。


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。会社名を非表示とする選択を行わない限り、プロフィール内の情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

ディスプレイ・ネームを選択してください



developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

必須フィールドは(*)で示されます。

3文字から31文字の範囲で指定し

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


送信されたすべての情報は安全です。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=WebSphere, Open source
ArticleID=358111
ArticleTitle=WebSphere Application Server で Spring および OpenJPA を使用する
publish-date=10292008