corner imagecorner image
IDEPlatformPluginsDocs & SupportCommunityPartners

NetBeans E コマースのチュートリアル - エンティティークラスおよびセッション Bean の追加

このページの内容は NetBeans IDE の version 6.8 および 6.9 が対象です

このチュートリアルユニットでは、EJB (Enterprise JavaBeans) および JPA (Java Persistence) テクノロジを紹介します。この中で、Java EE 開発に不可欠な 2 つの IDE のウィザードを使用します。これらを次に示します。

  • 「データベースからのエンティティークラス」ウィザード: 選択したデータベースの表ごとに、名前付きクエリー注釈、各列を表すフィールド、および外部キーを表す関係を備えた Java Persistence API エンティティークラスを作成します。
  • 「エンティティークラスのセッション Bean」ウィザード: エンティティークラスごとに、基本的なアクセスメソッドを備えた EJB セッションファサードを作成します。

これらの 2 つのウィザードは、アプリケーションのモデルをすばやく設定するための効率的な方法を提供します。構築しているアプリケーションの MVC 図をもう一度参照してください。MVC の構成内で、EJB セッション Bean および JPA エンティティークラスが対応する場所を確認できます。

AffableBean アプリケーションの MVC 図

このユニットでは、エンティティークラスを作成して、Java 上で affablebean データベースを表します。各エンティティークラスがデータベースの表を表す一方で、エンティティークラスのインスタンスは、データベースに保存 (つまり持続) 可能なレコードに対応します。アプリケーションのビジネスロジックはセッション Bean によってカプセル化され、(ここで示すように) エンティティーへの CRUD (Create-Read-Update-Delete) アクセスを可能にするファサードクラスとして使用したり、アプリケーション固有のアクションを実装するコードを含めたりできます (この例は「ユニット 9: トランザクションビジネスロジックの統合」に記載)。

このチュートリアルで構築するアプリケーションのライブデモを、「NetBeans E コマースのチュートリアルのデモアプリケーション」で確認できます。



ソフトウェアまたはリソース 必須バージョン
NetBeans IDE Java バンドル版、6.8 または 6.9
Java Development Kit (JDK) version 6
GlassFish サーバー v3 または Open Source Edition 3.0.1
MySQL データベースサーバー version 5.1
AffableBean プロジェクト スナップショット 3

注:

  • NetBeans IDE が正常に動作するには、JDK (Java Development Kit) が必要です。上記に一覧表示されているいずれのリソースもインストールされていない場合は、最初に JDK をダウンロードしてインストールするようにしてください。
  • NetBeans IDE の Java バンドル版には、このチュートリアルで構築するアプリケーションに必要な Java Web および EE テクノロジが含まれています。
  • NetBeans IDE の Java バンドル版には、このチュートリアルに必要な GlassFish サーバーも含まれています。GlassFish サーバーを別個にダウンロードすることもできますが、NetBeans ダウンロードに付属するバージョンを使用すると、IDE に自動的に登録されるので便利です。
  • このチュートリアルユニットは、以前のユニットを完了させていなくても進めることができます。そのために、データベースの準備や、IDE、GlassFish、および MySQL 間の接続の確立について説明した設定手順を参照してください。
  • AffableBean プロジェクトのスナップショット 4 をダウンロードでき、これは NetBeans IDE 6.9 を使用してこのユニットを完了させたあとの状態のプロジェクトに対応しています。

EJB および JPA テクノロジとは

このチュートリアルで今まで開発してきたプロジェクトは、Apache Tomcat などのサーブレットコンテナを使用して、Web ブラウザで実行できました。結局のところ、ここまでは JSTL およびサーブレットテクノロジを利用しただけで、JDBC を使用して直接データベースに接続しています。実際、アプリケーションのすべての側面 (スレッドの安全性、トランザクション、セキュリティーなど) のために手動でコーディングしながら、これらのテクノロジだけを使用してアプリケーションの開発を続けることも理論的には可能です。しかし、エンタープライズ Bean と JPA エンティティークラスを使用すると、すでに十分に試行された信頼できるソリューションを使用しながら、アプリケーションのビジネスロジックに集中できるようになります。以降の節ではこの 2 つのテクノロジを紹介し、EE 開発でこれらが担う役割を説明します。

エンタープライズ JavaBeans

EJB 製品の公式ページでは、エンタープライズ JavaBeans テクノロジを「分散型の、トランザクション可能な、セキュリティー保護された、可搬性があるアプリケーションの開発をすばやく簡単に行うことができるサーバー側コンポーネントアーキテクチャー」と説明しています。EJB (エンタープライズ Bean) をプロジェクトに適用しても、このテクノロジが提供するサービスは開発者にとって透過的なままであり、EJB を使用しない場合に必要になる、大量のボイラープレートコードを追加する退屈で間違えやすい作業を取り除くことができます。EE 開発の経験が少ない場合、作成する Java Web アプリケーションでの EJB の必要性を疑問に思うかもしれません。Debu Panda 氏、Reza Rahman 氏、および Derek Lane 氏による『EJB 3 In Action』の本では、EJB テクノロジの役割を次のようにうまく言い換えています。

中規模の、比較的単純な Web アプリケーションの開発のために EJB を使用することは大げさであると多くの人が考えていますが、これは大きな誤りです。家を建設するとき、すべてを一から造るわけではありません。代わりに資材を買ったり、必要に応じて請負業者のサービスを申し込んだりもします。同様に、エンタープライズアプリケーションを一から構築することはあまり現実的ではありません。ほとんどのサーバー側アプリケーションには、変動するビジネスロジック、アプリケーション状態の管理、リレーショナルデータベースに対する情報の格納および取得、トランザクションの管理、セキュリティーの実装、非同期処理の実行、システムの統合など、多くの共通点があります。

フレームワークとして、EJB コンテナはこの種の一般的な機能をすぐに使えるサービスとして提供しており、EJB コンポーネントが車輪を再発明することなくアプリケーション内でこれらのサービスを使用できるようになっています。たとえば、Web アプリケーションでクレジットカードモジュールを構築するときに、トランザクションやセキュリティーアクセス制御の管理のために複雑で間違えやすい大量のコードを書いているとします。これは、EJB コンテナが提供する宣言型のトランザクションやセキュリティーサービスを使用すれば避けられるはずです。EJB コンポーネントが EJB コンテナへ配備されると、これらのほか数々のサービスを使用できるようになります。これにより、高品質で豊富な機能を持つアプリケーションが、想像を大きく上回るほどの速さで書けるようになります。
[1]

EJB は、プロジェクトに組み込まれたコンポーネントまたは Java クラスと考えることも、多数のエンタープライズ関連のサービスを提供するフレームワークと考えることもできます。このチュートリアルで利用するサービスのいくつかについては、『EJB 3 In Action』で次のように説明されています。

  • プール: EJB プラットフォームは、EJB コンポーネントごとに、クライアントで共有されるコンポーネントインスタンスのプールを作成します。どの時点でも、プールされた各インスタンスを使用できるのは単一のクライアントだけです。インスタンスがクライアントへのサービスを終了すると、再生用にガベージコレクタによって破棄されるのではなく、再利用のためにプールに戻されます。
  • スレッドの安全性: EJB は、完全に見えない方法で、すべてのコンポーネントをスレッドセーフおよび高性能にします。これは、シングルスレッドのデスクトップアプリケーションのようにサーバーコンポーネントを開発できることを意味します。コンポーネント自体がどれほど複雑であっても、EJB によって確実にスレッドセーフになります。
  • トランザクション: EJB は宣言型のトランザクション管理をサポートします。宣言型のトランザクション管理では、コードの代わりに簡単な構成を使用してコンポーネントにトランザクション可能な動作を追加できます。実質的に、どのようなコンポーネントメソッドもトランザクション可能として指定できます。メソッドが正常に完了すると、EJB はトランザクションを確定して、メソッドによるデータの変更を永続的なものにします。それ以外の場合は、トランザクションがロールバックされます。コンテナ管理による EJB トランザクションについては、ユニット 9「トランザクションビジネスロジックの統合」で説明します。
  • セキュリティー: EJB は、JAAS (Java Authentication and Authorization Service) API との統合をサポートしているため、アプリケーションをセキュリティーコードで複雑にする代わりに、セキュリティーを完全に外部に切り離して簡単な構成でアプリケーションをセキュリティー保護できます。[2]ユニット 11「アプリケーションの保護」では、EJB の @RolesAllowed 注釈を示します。

Java Persistence

Java エンタープライズの文脈での「持続性」とは、Java オブジェクトに含まれるデータを自動的にリレーショナルデータベースに格納する動作を指します。JPA (Java Persistence API) は、開発者にとって透過的な方法で、アプリケーションが Java オブジェクトとリレーショナルデータベース間でデータを管理できるようにする ORM (Object-Relational Mapping: オブジェクト関係マッピング) テクノロジです。これは、データモデルをミラー化する一連の Java クラス (エンティティー) を作成および構成することで、JPA をプロジェクトに適用できることを意味します。そうすると、アプリケーションがデータベースに直接アクセスしているようにこれらのエンティティーにアクセスできます。

プロジェクトで JPA を使用すると、次のようなさまざまなメリットがあります。

  • JPA には、静的および動的クエリーのための、固有の充実した SQL のようなクエリー言語があります。JPQL (Java Persistence Query Language) を使用することで、アプリケーションが異なるデータベースベンダー間の可搬性を維持できます。
  • 低レベル、冗長、そして間違えやすい JDBC/SQL コードを書く作業を避けることができます。
  • JPA は、データのキャッシングおよびパフォーマンスの最適化のサービスを透過的に提供します。

セッション Bean とは

エンタープライズセッション Bean は、特定のビジネス処理を実行するためにクライアントによって呼び出されます。「セッション」という名前には、Bean インスタンスが「仕事の単位」の期間使用できるという意味が含まれています。EJB 3.1 仕様では、一般的なセッションオブジェクトを、次のような特長を持つものとして説明しています。

  • 単一のクライアントの代わりに実行
  • トランザクション対応可能
  • 配下のデータベースの共有データを更新
  • データベースの共有データを直接示すわけではないが、アクセスおよび更新ができる
  • 比較的存在期間が短い
  • EJB コンテナがクラッシュすると削除される。処理を続けるには、クライアントは新しいセッションオブジェクトを再確立する必要があります。

EJB は、ステートフルステートレス、およびシングルトンの 3 種類のセッション Bean を提供します。次の説明は、Java EE 6 チュートリアルの内容を元にしたものです。

  • ステートフル: Bean の状態は、複数のメソッド呼び出しにわたって維持されます。「状態」とは、そのインスタンス変数の値を示します。クライアントが Bean と対話するため、この状態はよく「対話形式の」状態と呼ばれます。
  • ステートレス: ステートレス Bean は、単一のメソッド呼び出しで発生し得る処理に使用されます。メソッドが処理を終了すると、クライアント固有の Bean の状態は保持されません。したがって、ステートレスセッション Bean は、クライアントと対話形式の状態を維持しません。
  • シングルトン: シングルトンセッション Bean は、アプリケーションごとに 1 度インスタンス化され、アプリケーションのライフサイクルの間存在します。シングルトンセッション Bean は、単一のエンタープライズ Bean インスタンスがクライアント間で共有され、並行してアクセスされる場合用に設計されています。

EJB セッション Bean の詳細については、「Java EE 6 チュートリアル: セッション Bean とは」を参照してください。

このチュートリアルの E コマースアプリケーション開発で使用するのは、ステートレスセッション Bean だけです。


仕様および実装について

EJB および JPA テクノロジは、次の仕様によって定義されています。

これらの仕様はテクノロジを定義しています。しかし、テクノロジをプロジェクトに適用するには、仕様の実装を使用する必要があります。仕様が確定すると、テクノロジの無償の実装であるリファレンス実装が含まれます。この概念がわかりにくい場合は、次のような例で考えてください。音楽作品 (つまり、ページ上の音符) とは、1 つの曲のことです。演奏者は、その曲を学んで演奏を録音するときに、曲の解釈を提供します。このように、音楽作品は技術仕様に対応し、演奏者の録音は仕様の実装に対応します。

Java 技術仕様と、その正式な標準化定義については、「Java コミュニティープロセスとは」を参照してください。

EJB および JPA 仕様の最終リリースのダウンロードページを調べると、次のリファレンス実装へのリンクが見つかります。

JPA 仕様の実装は持続性プロバイダと呼ばれており、JPA 2.0 仕様のリファレンス実装として選択された持続性プロバイダは EclipseLink です。

EJB リファレンス実装のリンクを調べると、EJB の実装だけでなく、プロジェクト GlassFish が提供するすべてのリファレンス実装が一覧表示されたページに移動します。この理由は、プロジェクト GlassFish が Java EE 6 プラットフォーム仕様 (JSR 316) のリファレンス実装を形成しているためです。このチュートリアルで E コマースプロジェクトを構築するのに使用する GlassFish v3 アプリケーションサーバー (Open Source Edition) には、プロジェクト GlassFish の元で開発されたすべてのテクノロジのリファレンス実装が含まれています。このことから、これは Java EE 6 コンテナと呼ばれています。

Java EE コンテナには、Web (サーブレット) コンテナ、EJB コンテナ、および持続性プロバイダの 3 つの必須コンポーネントが含まれています。E コマースアプリケーションの配備シナリオを次の図に示しています。このユニットで作成するエンティティークラスは、持続性プロバイダによって管理されます。このユニットで作成するセッション Bean は、EJB コンテナによって管理されます。ビューは、Web コンテナによって管理される JSP ページで描画されます。

GlassFish v3 Java EE コンテナ

エンティティークラスの追加

まず、IDE の「データベースからのエンティティークラス」ウィザードを使用して、affablebean スキーマに基づくエンティティークラスを生成します。このウィザードは、配下の持続性プロバイダに依存してこの作業を行います。

  1. IDE でプロジェクトスナップショットを開きます。IDE で、Ctrl-Shift-O (Mac の場合は âŚ�-Shift-O) を押し、ダウンロードしたファイルを解凍したコンピュータ上の場所に移動します。
  2. Ctrl-N (Mac の場合は ⌘-N) を押して、「ファイル」ウィザードを開きます。
  3. 「持続性」カテゴリから「データベースからのエンティティークラス」を選択します。「次へ」をクリックします。
  4. ステップ 2 の「データベース表」で、「データソース」ドロップダウンリストから「jdbc/affablebean」を選択します。ドロップダウンリストは、アプリケーションサーバーに登録されたデータソースによって生成されます。

    jdbc/affablebean データソースを選択すると、IDE によってデータベースがスキャンされ、「使用可能な表」区画にデータベースの表が一覧表示されます。
    「データベースからのエンティティークラス」ウィザード
  5. 「すべてを追加」ボタンをクリックしてから「次へ」クリックします。
  6. 「データベースからのエンティティークラス」ウィザードのステップ 3 は、NetBeans IDE 6.8 と 6.9 で少し異なります。使用する IDE のバージョンに応じて、以下の手順を実行します。

    NetBeans IDE 6.8

    「データベースからのエンティティークラス」ウィザード、ステップ 3: エンティティークラス
    1. 「パッケージ」フィールドに「entity」と入力します。ウィザードの完了時に、エンティティークラスの新しいパッケージが作成されます。
    2. 「持続性ユニットを作成」ボタンをクリックします。「持続性ユニットを作成」ダイアログが開きます。
      「持続性ユニットを作成」ダイアログ
      持続性ユニットとは、アプリケーション内に存在するエンティティークラスのコレクションのことです。上のダイアログで、持続性プロバイダが持続性ユニットの構成設定を指定するために使用する persistence.xml ファイルが生成されます。このプロジェクトに関連付けられたサーバーには、「EclipseLink (JPA 2.0)」がデフォルトで選択されています。「表生成の方針」は「なし」のままにします。これにより、持続性プロバイダがデータベースに影響しないようになります。(たとえば、持続性プロバイダに、データベースを削除してから既存のエンティティークラスに基づいてデータベースを再作成させる場合は、方針を「ドロップして作成」に設定します。この場合、このアクションはプロジェクトが配備されるたびに行われます。)
    3. 「作成」をクリックします。
    4. ステップ 3 の「エンティティークラス」に戻ると、エンティティーのクラス名がデータベースの表に基づいたものになっています。たとえば、CustomerOrder エンティティーは customer_order データベース表にマップされています。また、「持続フィールド用の NamedQuery 注釈を生成」オプションがデフォルトで選択されています。このチュートリアルの後半で、さまざまな名前付きクエリーを使用します。
    5. 下の手順 7 に進みます。

    NetBeans IDE 6.9

    「データベースからのエンティティークラス」ウィザード、ステップ 3: エンティティークラス
    1. 「パッケージ」フィールドに「entity」と入力します。ウィザードの完了時に、エンティティークラスの新しいパッケージが作成されます。
    2. 次の点に注意してください。
      • エンティティーのクラス名は、データベース表に基づきます。たとえば、CustomerOrder エンティティーは customer_order データベース表にマップされます。
      • 「持続フィールド用の NamedQuery 注釈を生成」オプションはデフォルトで選択されています。このチュートリアルの後半で、さまざまな名前付きクエリーを使用します。
      • 「持続性ユニットを作成」オプションはデフォルトで選択されています。持続性ユニットは、アプリケーション内に存在するエンティティークラスのコレクションです。持続性ユニットは、持続性プロバイダが読み取る persistence.xml 構成ファイルで定義されます。したがって、このオプションを有効にすることは、ウィザードによって persistence.xml ファイルが生成されてデフォルト設定が割り当てられることにもなります。
  7. 「完了」をクリックします。JPA エンティティークラスが affablebean データベース表に基づいて生成されます。「プロジェクト」ウィンドウで新しく作成された entity パッケージを展開すると、エンティティークラスを調べることができます。また、「構成ファイル」ノードの下には新しい持続性ユニットがあります。
    「プロジェクト」ウィンドウ - プロジェクトに表示されたエンティティークラス

    ウィザードによって、追加のエンティティークラス OrderedProductPK が生成されています。データモデルの ordered_product 表は、customer_order および product の両方の表の主キーから成る複合主キーを使用することを思い出してください (「データモデルの設計 - 多対多の関係の作成」を参照)。このため、持続性プロバイダは複合キーのための別個のエンティティークラスを作成して、OrderedProduct エンティティーに埋め込みますOrderedProduct をエディタで開くと、これを調べることができます。JPA は、組み込み可能なクラスが複合主キーであることを示すために、@EmbeddedId 注釈を使用します。
    public class OrderedProduct implements Serializable {
        private static final long serialVersionUID = 1L;
        @EmbeddedId
        protected OrderedProductPK orderedProductPK;

    @EmbeddedId 注釈で Ctrl- スペースキーを押して API ドキュメントを呼び出します。

    @EmbeddedId で呼び出された API ドキュメント
  8. エディタで持続性ユニット (persistence.xml) を開きます。IDE には「XML」ビューに加え、持続性ユニットのための「デザイン」ビューも用意されています。「デザイン」ビューは、プロジェクトの持続性プロバイダの管理構成を変更するための便利な方法を提供します。
    AffableBeanPU 持続性ユニットの「デザイン」ビュー
  9. AffableBeanPU 持続ユニットの最上部にある「XML」タブをクリックして、「XML」ビューを開きます。ファイルに次のプロパティーを追加します。
    <persistence-unit name="AffableBeanPU" transaction-type="JTA">
      <jta-data-source>jdbc/affablebean</jta-data-source>
      <properties>
        <property name="eclipselink.logging.level" value="FINEST"/>
      </properties>
    </persistence-unit>
    アプリケーションを実行したときに持続性プロバイダによって生成される出力がすべて表示されるように、ロギングレベルのプロパティーを FINEST に設定しました。これによって、持続性プロバイダがデータベースで使用している SQL が確認できるようになり、どのようなデバッグが必要になったとしても、デバッグが容易になります。

    ロギングの説明およびすべてのロギング値の一覧については、公式の EclipseLink ドキュメント「ロギングの構成方法」を参照してください。


セッション Bean の追加

この節では、IDE の「エンティティークラスのセッション Bean」ウィザードを使用して、作成したばかりのエンティティークラスごとに EJB セッションファサードを生成します。各セッション Bean には、それぞれのエンティティークラスのための基本的なアクセスメソッドが含まれます。

セッションファサードとは、エンタープライズブループリントプログラムで公表されているデザインパターンです。コア J2EE パターンカタログに記載されているように、これは多層アプリケーション環境で起こる次のような一般的な問題の解決を図るものです。

  • 密結合による、クライアントとビジネスオブジェクト間の直接の依存関係
  • クライアントとサーバー間の過多のメソッド呼び出しによる、ネットワークパフォーマンスの問題
  • クライアントアクセス方針が一貫しないことによる、ビジネスオブジェクトの誤用

セッションファサードは、配下のビジネスオブジェクトの相互作用を抽象化して、必要な機能だけを使用できるようにするサービスレイヤーを提供します。これにより、参加者間の複雑な相互作用がクライアントから見えなくなります。このようにして、セッション Bean (セッションファサードを表します) はビジネスオブジェクト間の関係を管理します。また、セッション Bean は、ワークフローの必要に応じて参加者を作成、配置、変更、および削除することで、参加者のライフサイクルも管理します。

  1. Ctrl-N (Mac の場合は ⌘-N) を押して、「ファイル」ウィザードを開きます。
  2. 「持続性」カテゴリを選択してから「エンティティークラスのセッション Bean」を選択します。
    「ファイル」ウィザード: 「持続性」カテゴリ、「エンティティークラスのセッション Bean」のファイルの種類
  3. 「次へ」をクリックします。
  4. ステップ 2 の「エンティティークラス」では、左側の「利用可能なエンティティークラス」の下に、プロジェクトに含まれているすべてのエンティティークラスが一覧表示されています。「すべてを追加」をクリックします。すべてのエンティティークラスが、右側の「選択されているエンティティークラス」の下に移動します。
  5. 「次へ」をクリックします。
  6. ステップ 3 の「生成されるセッション Bean」で、「パッケージ」フィールドに「session」と入力します。
    「エンティティークラスのセッション Bean」ウィザード - ステップ 3: 「生成されるセッション Bean」

    注: このウィザードを使用して、セッション Bean のローカルおよびリモートインタフェースを生成できます。セッション Bean をインタフェースとしてプログラミングするメリットはありますが (たとえば、ビジネスオブジェクトの相互作用をインタフェースの後ろに隠すと、クライアントをビジネスロジックからさらに切り離すことができます。これにより、必要に応じてアプリケーション用にインタフェースの複数の実装をコーディングできるようになります)、これについてはこのチュートリアルでは扱いません。3.1 より前のバージョンの EJB では、セッション Bean ごとにインタフェースを実装する必要があります

  7. 「完了」をクリックします。IDE によって、プロジェクトに含まれるエンティティークラスごとにセッション Bean が生成されます。「プロジェクト」ウィンドウで、新しい session パッケージを展開してセッション Bean を調べます。

    NetBeans 6.8 NetBeans 6.9
    「プロジェクト」ウィンドウ - プロジェクトに表示されたセッション Bean 「プロジェクト」ウィンドウ - プロジェクトに表示されたセッション Bean

    注: 上記のように、NetBeans IDE 6.9 では、「エンティティークラスのセッション Bean」ウィザードがファサードクラスを生成するようにわずかな改善が提供されます。つまり、すべてのクラスに共通するボイラープレートコードが AbstractFacade という名前の抽象クラスに取り出されます。version 6.9 を使用している場合は、生成されたいずれかのファサードクラス (AbstractFacade を除く) を開いてください。そのクラスが AbstractFacade を継承していることがわかります。

  8. エディタでセッションファサード (たとえば、ProductFacade) を開きます。生成されたすべてのセッションファサードは、@PersistenceContext 注釈を使用して EntityManager をインスタンス化します。
    @PersistenceContext(unitName = "AffableBeanPU")
    private EntityManager em;
    @PersistenceContext 注釈は、コンテナ管理による EntityManager をクラスに注入するために使用されます。つまり、必要に応じて GlassFish の EJB コンテナを利用して EntityManager を開いたり閉じたりします。unitName 要素には、アプリケーションの persistence.xml ファイルで定義されている AffableBeanPU 持続性ユニットを指定します。

    EntityManager は Java Persistence API の重要なコンポーネントであり、データベースで持続性アクションを実行する役割を果たします。『EJB 3 In Action』の本では、EntityManager を次のように説明しています。
    JPA EntityManager インタフェースは、実際に持続性サービスを提供することでエンティティーを管理します。エンティティーは、JPA プロバイダに自身がデータベースにどのようにマップされているかを伝えますが、自身は持続しません。EntityManager インタフェースは、エンティティーの ORM メタデータを読み取って、持続性オペレーションを実行します。

アプリケーションに、JPA エンティティークラスの形式で affablebean データベースの持続性モデルが含まれるようになりました。また、エンティティークラスにアクセスするために使用できるエンタープライズ Bean で構成されるセッションファサードも含まれています。次の節では、セッション Bean およびエンティティークラスを使用してデータベースにアクセスする方法を示します。


EJB によるデータへのアクセス

前のチュートリアルユニットで、アプリケーションからデータベースにアクセスする方法を学習しました。この方法では、GlassFish にデータソースを構成し、アプリケーションの配備記述子にリソース参照を追加してから、アプリケーションの JSP ページで JSTL の <sql> タグを使用することで、データベースにアクセスしました。データベースのデータを含むプロトタイプをすばやく設定できるため、これは重要な手法です。しかし、この手法では維持管理や拡大縮小が難しくなるため、中規模から大規模のアプリケーションや、複数開発者のチームによって管理されるアプリケーションに使用することは現実的ではありません。その上、多層にわたるアプリケーションを開発している場合や、MVC パターンに従っている場合、データにアクセスするコードをフロントエンドに保持することは望ましくありません。エンタープライズ Bean と持続性モデルを使用すると、プレゼンテーションコンポーネントとモデルコンポーネントを効果的に切り離して、MVC パターンにより準拠できるようになります。

次の手順では、AffableBean プロジェクトでセッションおよびエンティティー Bean を使い始めるための方法を示します。以前にインデックスページおよびカテゴリページのために設定した JSTL データアクセスロジックは削除することになります。代わりに、セッション Bean によって提供されるデータアクセスメソッドを利用して、スコープ指定された変数にデータを格納し、フロントエンドのページビューからデータを取り出せるようにします。まずインデックスページに取り組んでから、より複雑なカテゴリページに進みます。

インデックスページ

インデックスページには、4 つの製品カテゴリのデータが必要です。現時点の設定では、インデックスページが要求されるたびに、JSTL の <sql> タグがカテゴリの詳細をデータベースに問い合わせます。この情報はほとんど変更されないため、パフォーマンスの観点からすると、アプリケーションが配備されたあとに一度だけクエリーを実行して、アプリケーションスコープ指定された属性にデータを格納する方が効果的です。このコードを ControllerServletinit メソッドに追加することで、これを実現できます。

  1. 「プロジェクト」ウィンドウで、「ソースパッケージ」>「controller」>「ControllerServlet」ノードをダブルクリックしてエディタで開きます。
  2. CategoryFacade のインスタンスを宣言して、@EJB 注釈をインスタンスに適用します。
    public class ControllerServlet extends HttpServlet {
    
        @EJB
        private CategoryFacade categoryFacade;
    
        ...
    }
    @EJB 注釈は、CategoryFacade という名前の EJB で categoryFacade 変数をインスタンス化するように EJB コンテナに指示します。
  3. IDE のヒントを使用して、次のインポート文を追加します。
    • javax.ejb.EJB
    • session.CategoryFacade

    Ctrl-Shift-I (Mac の場合は ⌘-Shift-I) を押すと、必要なインポートが自動的にクラスに追加されます。

  4. 以下の init メソッドをクラスに追加します。Web コンテナは、その init メソッドを呼び出してサーブレットを初期化します。これは、サーブレットがロードされてからサービス要求を開始するまでの間に 1 度だけ起こります。
    public class ControllerServlet extends HttpServlet {
    
        @EJB
        private CategoryFacade categoryFacade;
    
        public void init() throws ServletException {
    
            // store category list in servlet context
            getServletContext().setAttribute("categories", categoryFacade.findAll());
        }
    
        ...
    }
    ここで、Category のすべてのレコードをデータベースに問い合わせる、ファサードクラスの findAll メソッドを適用します。そのあと、結果として得られた Category オブジェクトの List を、「categories」の文字列で参照できる属性として設定します。ServletContext に参照を置くと、その参照はアプリケーション全体のスコープで存在することになります。

    findAll メソッドのメソッド署名をすばやく判定するには、Ctrl キー (Mac の場合は ⌘) を押しながらカーソルをメソッドの上に移動します。(以下の画像は、NetBeans IDE 6.8 を使用して表示されるポップアップを示しています。)

    ポップアップにメソッド署名が表示されたエディタ
    ハイパーリンクをクリックすると、メソッドに直接移動できます。
  5. IDE のヒントを使用して、@Overrides 注釈を追加します。init メソッドは、HttpServlet のスーパークラスである GenericServlet によって定義されます。
    エディタに表示されたヒント
    注釈を追加することは必須ではありませんが、これを行うと次のようないくつかの利点が得られます。
    • 本当にオーバーライドするつもりのメソッドをオーバーライドしているかどうかを確認するためのコンパイラチェックを使用できるようになります。
    • ソースコードのメソッドがオーバーライドされている状態が明確になるため、読みやすさが向上します。

    注釈の詳細については、「Java チュートリアル: 注釈」を参照してください。

  6. これで、カテゴリの一覧を含むアプリケーションスコープ指定された属性を設定したので、新しく作成した属性にアクセスするようにインデックスページを変更します。

    「プロジェクト」ウィンドウで「Web ページ」>「index.jsp」ノードをダブルクリックして、このファイルをエディタで開きます。
  7. ファイルの最上部に一覧表示されている <sql:query> 文をコメントアウト (または削除) します。エディタでコードをコメントアウトするには、コードを強調表示してから Ctrl-/ (Mac の場合は ⌘-/) を押します。
    エディタに表示されたコメントアウトされたスニペット
  8. <c:forEach> の開始タグを変更して、この items 属性が、新しいアプリケーションスコープ指定された categories 属性を参照するようにします。
    <c:forEach var="category" items="${categories}">
  9. プロジェクトの Web 配備記述子を開きます。Alt-Shift-O (Mac の場合は Ctrl-Shift-O) を押して、「ファイルに移動」ダイアログで「web」と入力してから「了解」をクリックします。
    「ファイルに移動」ダイアログ
  10. <resource-ref> エントリをコメントアウト (または削除) します。このエントリは、サーバーに登録されたデータソースを <sql> タグで識別するために必要でした。現時点では、JPA に依存してデータベースにアクセスしており、持続性ユニットに jdbc/affablebean データソースがすでに指定されています (上記のプロジェクトの持続性ユニットの「デザイン」ビューを参照)。

    <resource-ref> エントリ全体を強調表示してから Ctrl-/ (Mac の場合は ⌘-/) を押します。
    <!-- <resource-ref>
             <description>Connects to database for AffableBean application</description>
             <res-ref-name>jdbc/affablebean</res-ref-name>
             <res-type>javax.sql.DataSource</res-type>
             <res-auth>Container</res-auth>
             <res-sharing-scope>Shareable</res-sharing-scope>
         </resource-ref> -->
  11. プロジェクトを実行します。「プロジェクトを実行」(「プロジェクトを実行」ボタン) ボタンをクリックします。プロジェクトのインデックスページがブラウザで開き、4 つのすべてのカテゴリ名および画像が表示されるのが確認できます。
    カテゴリの詳細を表示するインデックスページ

カテゴリページ

カテゴリページが正しく描画されるには、次の 3 つのデータが必要です。

  1. カテゴリデータ: 左の列のカテゴリボタン用
  2. 選択されたカテゴリ: 選択されたカテゴリは左の列で強調表示され、選択されたカテゴリの名前は製品の表の上に表示される
  3. 選択されたカテゴリの製品データ: 製品の表に表示された製品用

3 つのデータを 1 つずつ処理していきましょう。

カテゴリデータ

インデックスページのために作成した、アプリケーションスコープ指定された categories 属性をカテゴリデータ用に再利用します。

  1. エディタで category.jsp を開き、ファイルの最上部に一覧表示されている JSTL の <sql> 文を (Ctrl-/、Mac の場合は ⌘-/ で) コメントアウトします。
    エディタでコメントアウトされた <sql> 文
  2. <c:forEach> の開始タグを変更して、この items 属性が、アプリケーションスコープ指定された categories 属性を参照するようにします (上記で index.jsp に対して行なった手順と同じです)。
    <c:forEach var="category" items="${categories}">
  3. プロジェクトを実行して、カテゴリページの現在の状態を調べます。「プロジェクトを実行」(「プロジェクトを実行」ボタン) ボタンをクリックします。プロジェクトのインデックスページがブラウザで開いたら、4 つのカテゴリのいずれかをクリックします。左の列にカテゴリボタンが表示され、想定したとおりに機能します。
    左の列にカテゴリボタンが表示されたカテゴリページ

選択されたカテゴリ

選択されたカテゴリを取得するために、カテゴリ ID と要求クエリー文字列が一致する Category を見つけるためにすでに作成してある categoryFacade を使用できます。

  1. エディタで ControllerServlet を開きます (すでに開いている場合は、Ctrl-Tab キーを押してポップアップリストから選択します)。
  2. 選択されたカテゴリを取得する機能の実装を開始します。TODO: Implement category request というコメントを見つけ、それを削除して以下の (太字の) コードを追加します。
    // if category page is requested
    if (userPath.equals("/category")) {
    
        // get categoryId from request
        String categoryId = request.getQueryString();
    
        if (categoryId != null) {
    
        }
    
    // if cart page is requested
    } else if (userPath.equals("/viewCart")) {
    要求で getQueryString() を呼び出すことで、要求されたカテゴリ ID を取得します。

    注: 左の列のカテゴリボタン内で選択されたカテゴリを判定するためのロジックは、EL 式を使用する category.jsp ですでに実装されています。これは、サーブレットで getQueryString() を呼び出すことに相当します。この EL 式は pageContext.request.queryString です。

  3. if 文の中に、次のコード行を追加します。
    // get categoryId from request
    String categoryId = request.getQueryString();
    
    if (categoryId != null) {
    
        // get selected category
        selectedCategory = categoryFacade.find(Short.parseShort(categoryId));
    }
    CategoryFacadefind メソッドを使用して、要求されたカテゴリ ID に基づく Category オブジェクトを取得します。categoryId を、Category エンティティークラスの id フィールドに使用される型である Short にキャストする必要があります。
  4. 左マージンにあるバッジ (ヒントバッジ) をクリックしてエディタのヒントを使用し、selectedCategorydoGet メソッド内のローカル変数として宣言します。
    エディタのヒント
    selectedCategory はこのクラスにまだインポートされていない Category 型であるため、IDE は entity.Category のインポート文をファイルの先頭に自動的に追加します。
  5. 取得した Category オブジェクトを要求スコープに置くために次の行を追加します。
    // get categoryId from request
    String categoryId = request.getQueryString();
    
    if (categoryId != null) {
    
        // get selected category
        selectedCategory = categoryFacade.find(Short.parseShort(categoryId));
    
        // place selected category in request scope
        request.setAttribute("selectedCategory", selectedCategory);
    }
  6. エディタで category.jsp に切り替えます (Ctrl-Tab キー を押してポップアップリストから選択します)。
  7. <p id="categoryTitle"> を検索して、次のように変更します。
    <p id="categoryTitle">
        <span style="background-color: #f5eabe; padding: 7px;">${selectedCategory.name}</span>
    </p>
    現時点で、ControllerServlet から要求スコープに置いたばかりの selectedCategory 属性を使用しています。EL 式の中で「.name」を使用すると、指定された Category オブジェクトで getName メソッドが呼び出されます。
  8. ブラウザに戻ってカテゴリページを再表示します。選択されたカテゴリの名前がページに表示されるようになっています。
    選択されたカテゴリの名前が表示されたカテゴリページ

選択されたカテゴリの製品データ

選択されたカテゴリのすべての製品を取得するために、Category エンティティーの getProductCollection() を使用します。まず、selectedCategory 上でこのメソッドを呼び出して、selectedCategory に関連付けられたすべての Product のコレクションを取得します。次に製品のコレクションを要求スコープ内の属性として格納し、最後にスコープ指定された属性を category.jsp ページビューから参照します。

  1. ControllerServlet では、カテゴリ要求を管理する以下の文をコードに追加します。
    // if category page is requested
    if (userPath.equals("/category")) {
    
        // get categoryId from request
        String categoryId = request.getQueryString();
    
        if (categoryId != null) {
    
            // get selected category
            selectedCategory = categoryFacade.find(Short.parseShort(categoryId));
    
            // place selected category in request scope
            request.setAttribute("selectedCategory", selectedCategory);
    
            // get all products for selected category
            categoryProducts = selectedCategory.getProductCollection();
        }
    ここで getProductCollection() を呼び出すことにより、selectedCategory に関連付けられたすべての Product のコレクションを取得できます。
  2. エディタのヒントを使用して、categoryProductsdoGet メソッドのローカル変数として定義します。
    エディタのヒント
  3. 要求スコープに Product のコレクションを置いて、アプリケーションのフロントエンドから取得できるようにします。
    // if category page is requested
    if (userPath.equals("/category")) {
    
        // get categoryId from request
        String categoryId = request.getQueryString();
    
        if (categoryId != null) {
    
            // get selected category
            selectedCategory = categoryFacade.find(Short.parseShort(categoryId));
    
            // place selected category in request scope
            request.setAttribute("selectedCategory", selectedCategory);
    
            // get all products for selected category
            categoryProducts = selectedCategory.getProductCollection();
    
            // place category products in request scope
            request.setAttribute("categoryProducts", categoryProducts);
        }
  4. エディタで category.jsp ファイルを開き、製品の表を次のように変更します。
    <table id="productTable">
    
        <c:forEach var="product" items="${categoryProducts}" varStatus="iter">
    <c:forEach> タグは categoryProducts のコレクションを参照するようになりました。c:forEach ループはコレクションに含まれる各 Product オブジェクトに対して繰り返され、それに応じてデータを抽出するようになりました。
  5. F6 (Mac の場合は fn-F6) を押してプロジェクトを実行します。ブラウザでカテゴリページに移動すると、各カテゴリですべての製品が表示されるようになっています。
    表の製品が表示されたカテゴリページ

このチュートリアルユニットでは、JPA および EJB テクノロジを簡単に紹介しました。また、Java 仕様の役割について、およびそのリファレンス実装を GlassFish アプリケーションサーバーが使用する方法についても説明しました。そのあと、プロジェクトデータベースの Java 実装を提供する一連の JPA エンティティークラスを作成する方法を示しました。さらに、セッションファサードパターンに従って、エンティティークラス上に存在し、それらへの便利なアクセスを可能にする一連の EJB セッション Bean を作成する方法を示しました。最後に、AffableBean プロジェクトを変更して、インデックスページおよびカテゴリページで必要なデータベースへのアクセスに、新しいセッション Bean およびエンティティーを利用するようにしました。

AffableBean プロジェクトのスナップショット 4 をダウンロードでき、これは NetBeans IDE 6.9 を使用してこのユニットを完了させたあとの状態のプロジェクトに対応しています。

次のユニットでは、セッション管理について学習し、ユーザーがサイトの中でクリックして行なったアクションをアプリケーションに覚えさせる方法について学習します。これは、E コマースアプリケーションでショッピングカート機構を実装するためのキーポイントです。



関連項目

NetBeans リソース

EJB リソース

JPA リソース

GlassFish リソース

技術記事

書籍


参考資料

  1. ^ 出典: 『EJB 3 In Action』の第 1 章、1.1.2 節: 「EJB as a framework」
  2. ^ ほかにも EJB によって提供される多くのサービスがあります。より総合的な一覧については、『EJB 3 In Action』の第 1 章、1.3.3 節: 「Gaining functionality with EJB services」を参照してください。