咖啡日语论坛

 找回密码
 注~册
搜索
查看: 5944|回复: 3

EJB3.0入門講座

[复制链接]
发表于 2007-5-25 13:30:12 | 显示全部楼层 |阅读模式
回复

使用道具 举报

 楼主| 发表于 2007-5-25 13:33:44 | 显示全部楼层
複雑さを捨てて使いやすさに徹したEJB 3.0


 今,Enterprise JavaBeans(EJB)の新しいバージョンであるEJB 3.0が技術者の注目を集めています。これまで複雑で扱いにくいと批判されてきたEJBが,今回のバージョンアップでその批判に応えて大きく変わろうとしているからです。
 EJB3.0の仕様の最終リリースは2006年第1四半期に予定されています。本稿執筆時点(2005年12月11日現在)では最終リリースはまだ行われていません(6月現在Final Releaseあり)。しかし,現時点でドラフト段階の仕様に対応したEJB3.0の実装がいくつか公開されています。
 この連載では,米JBossが開発した「Embeddable EJB 3.0」という実行環境を使い,EJB 3.0の新しいプログラミング・モデルを紹介します。EJBを使ったことがある方もない方も,ぜひEJB 3.0の新しいプログラミング・モデルを体験してください。
欠点が多かった従来のEJB EJBは,企業アプリケーションを構築するためのコンポーネント・ベースのアーキテクチャです。EJBのコンポーネントが実行環境であるEJBコンテナの中で実行されます。
 EJBは標準化されたアーキテクチャですが,それにもかかわらず,現在それほど広く使われていると言えません。それには様々な理由があります。
 まず,多くの規約が存在し,開発やメンテナンスがしにくかった点が挙げられます。EJBのコンポーネントはJavaで記述しますが,通常のJavaプログラミングにはない規約が多数ありました。開発者はその規約をよく知っていることが必要でした。そのため開発が難しく,同様の理由でメンテナンスも大変でした。EJBのコンポーネントは,アプリケーション・サーバー上に配置しなければ実際に動かせなかったため,単体テストを行いにくいという問題もありました。
 効果を発揮するアプリケーションが限定的だったということもあります。企業アプリケーションといっても,アプリケーションによって要件は様々です。例えば,分散オブジェクト通信や非同期処理などの要件を含むアプリケーションにとってEJBは重要なアーキテクチャです。その一方で,EJBがもたらす効果を必要としないアプリケーションも多くありました。単一のサーバー上で動き,非同期処理が不要なアプリケーションなどです。そうしたものでは従来,EJBは有効な選択肢ではありませんでした。
大きく改良されたEJB 3.0 EJB 3.0には以前のEJBと比べて大きく異なる点があります。それはプログラミング・モデルです。EJB 3.0ではプログラミングが格段に簡単になりました(図1)。EJB 2.1以前のEJBを使った開発では,開発者がEJBの規約を意識しながらプログラミングしなければなりませんでした。しかし,EJB3.0を使った開発では,それらの規約のほとんどを意識する必要がありません。以前よりもずいぶんシンプルにEJBのコンポーネントを開発できるようになっています。EJB 3.0の主な特徴について説明しましょう。
[size=-1]図1 EJB 2.1以前とEJB 3.0の比較 [画像のクリックで拡大表示]

(1)EJBのコンポーネントを通常のJavaオブジェクトとして記述できる
 EJB2.1以前では,EJBのコンポーネントはjavax.ejbパッケージ内のインタフェースを実装し,コンポーネント・インタフェースというEJBの仕様にのっとったインタフェースに対応づけられることが必須でした。また,トランザクションやセキュリティといったEJBの実行時情報を,デプロイメント記述子と呼ばれるXMLの設定ファイルに記述する必要がありました。
 これに対してEJB3.0のコンポーネントは,javax.ejbパッケージ内のインタフェースやコンポーネント・インタフェースを必要としません。コンポーネントの作成には通常のインタフェースとクラスを使用します。ここで「通常の」と言っているのは,「ある特定のクラスやインタフェースを使用しなければならないという規約がない」という意味です*1。EJB 3.0のコンポーネントが実装するインタフェースはビジネス・インタフェースと呼ばれ,コンポーネントのクラス自身はBeanクラスと呼ばれます。
 また,XMLによる設定が必須ではなくなりました。代わりに「アノテーション」と呼ばれる注釈をソースコードに書き込むことで,EJBの実行時情報を設定できるようになりました。アノテーションはJ2SE 5から導入された技術で,ソースコード上に記述する付加的な情報のことです。
 EJB 3.0のコンポーネントの例をリスト1に示しました。このコードには,「@Stateless」という文字が含まれています。この「@」ではじまる文字情報がアノテーションです。リスト1のアノテーションは,CalculatorBeanクラスがステートレス・セッションBeanという種類のEJBクラスであることを示しています。
[size=-1]リスト1 EJB 3.0のステートレス・セッションBeanの例(CalculatorBean.java) [クリックでリストをテキスト表示]
 EJB2.1以前のように設定情報をデプロイメント記述子に記述すると,情報がソースコードから離れた場所に記述されることになり,EJBの挙動を把握しづらくなります。一方,EJB 3.0ではアノテーションを使うため,ソースコードに近い場所に設定情報を記述でき,見通しがよくなります。
(2)EJBのコンポーネントを簡単に呼び出せる
 EJB 2.1以前では,EJBのコンポーネントを呼び出すには,コンポーネントの参照をホーム・インタフェースと呼ばれるインタフェースを使って取得する必要がありました。
 EJB3.0では手順が大きく変わり,ホーム・インタフェースが不要になりました。特に,あるEJBコンポーネントから別のEJBコンポーネントを呼び出す手順が簡単になっています。このとき必要なのは,呼び出し元のソースコードに@EJBというアノテーションを記述して呼び出し先コンポーネントを使うことを示すだけです。あとは,通常通りメソッドを実行するコードを書きます。呼び出し元には,呼び出し先のコンポーネントの参照を取得するためのコードは必要ありません。@EJBというアノテーションを使うことで,実行環境が適切なタイミングで呼び出し先のコンポーネントの参照を呼び出し元にセットするからです。これはDI(Dependency Injection,依存性の注入)と呼ばれる機能です。DIを利用すれば,単体テストも行いやすくなります。
(3)Javaオブジェクトとデータベース間のデータ変換を簡潔に行える
 EJB2.1以前ではO/Rマッピングと永続化を実現する標準的な手段はエンティティBeanでした。しかし,エンティティBeanはそもそも分散コンポーネントとしてデザインされていたため,O/Rマッピングと永続化以外の機能も豊富に持っています。このため,O/Rマッピングと永続化のためだけにエンティティBeanを利用する場合でもコンポーネント・インタフェースを作成する必要があるなど考慮しなければならない事項が多く,扱いにくいものでした。
 EJB 3.0では,O/Rマッピングと永続化に関する機能が「Java PersistenceAPI」という新しい仕様としてEJBから独立することになりました。PersistenceAPIはサーバーサイド環境とスタンドアロン環境に共通したJava標準のAPIです。アーキテクチャにEJB3.0を使用しない場合でも利用できます。ただ,Persistence APIはEJB3.0での一般的なデータベース・アクセスの方法であり,トランザクションなどEJB 3.0の仕様に関連する事柄も多いので,この連載ではEJB3.0と一緒に説明します。
 PersistenceAPIはJDBCのAPIより上位のAPIです。JDBCのAPIを直接使う場合に比べ,シンプルなプログラム・コードでデータベースのデータをJavaオブジェクトに変換したり,Javaオブジェクトのデータをデータベースに格納したりできます。PersistenceAPIを使う場合にも,アノテーションが重要な役割を果たします。開発者はデータベースのテーブルに対応するJavaクラスを作成し,そのクラスにアノテーションでデータベースとJavaオブジェクトのマッピング情報を記述できます。
 ここまでの説明で示したように,EJB3.0は以前のEJBから複雑さの要因を取り除いたものです。これよってEJBを適用できるアプリケーションの裾野が広がったといえます。例えば,トランザクションなどアプリケーション・サーバーの一部のサービスしか利用しないためEJBを使う必要がなかったアプリケーションでもEJB3.0が有効な選択肢になります。なぜなら,多くの場合,EJB 3.0を使うことでEJB3.0を使わない場合よりも簡潔にアプリケーションを構成できるからです。
 こうした利点は,実際にEJB 3.0のコードを動かしてみると実感できます。そこで,EJB 3.0のコンポーネントを作成/実行するのに必要な実行環境を用意してみましょう。
EJB 3.0の動作環境をセットアップする この連載では,Embeddable EJB 3.0を使用します。特徴は,アプリケーション・サーバーを使用しなくてもEJB 3.0のコンポーネントを動かすことができることです*2。アプリケーション・サーバーの起動やアプリケーション・サーバーへのコンポーネントの配備が必要ないため,EJB 3.0を手軽に体験できます。
 12月11日現在,Embeddable EJB3.0の最新バージョンはalpha3です。実装されていない機能や制限がいくつかありますが,EJB3.0の基本機能は十分試せます。Embeddable EJB 3.0で物足りなくなったら,JBoss ApplicationServerに挑戦してみてください。
 OSはWindowsを想定しています*3。まず,JDK 5.0とEclipse 3.1をインストールしてください。次に,Eclipseを起動し,Eclipse上でJavaプロジェクトを新規作成します。「プロジェクト名」は「try-ejb3」,「JDK準拠」は「5.0」,「プロジェクト・レイアウト」は「別のソースおよび出力フォルダーを作成」と設定してください。Eclipseのウィザードでは図2のようになります。JDKに5.0が設定されていることを必ず確認してください。
[size=-1]図2 EclipseのウィザードでJavaプロジェクトを新規作成しているところ
 次がEmbeddable EJB 3.0の用意です。http://www.jboss.com/products/list/downloads#EJB3からダウンロードします。ダウンロードするファイルはjboss-EJB-3.0_Embeddable_ALPHA_3.zipです。ダウンロードしたら解凍し,その中に含まれるフォルダを図3の手順に従ってEclipseのtry-EJB3プロジェクトにコピーし,ビルド・パスを設定します。その際に,jndi.properties(リスト2)という設定ファイルを新たに作成します*4。Eclipseのパッケージ・エクスプローラーで見た場合,プロジェクトの構成は図4のようになります*5
[size=-1]図3 JavaのプロジェクトにEmbeddable EJB 3.0を組み込む手順


[size=-1]リスト2 Embeddable EJB 3.0で使う設定ファイル(jndi.properties) [クリックでリストをテキスト表示]


[size=-1]図4 Eclipseのパッケージ・エクスプローラーで見たプロジェクトの構成

EJB 3.0で「Hello!」を表示する では,EJB 3.0を使用した簡単なプログラムを動かしてみます。まず,先ほど作成したtry-ejb3プロジェクトの配下にパッケージを作成します。ここではstep1というパッケージ名にします。そこにHelloインタフェース(リスト3),HelloBeanクラス(リスト4),Mainクラス(リスト5)をそれぞれ作成してください。
[size=-1]リスト3 Helloインタフェース(Hello.java) [クリックでリストをテキスト表示]


[size=-1]リスト4 HelloBeanクラス(HelloBean.java) [クリックでリストをテキスト表示]


[size=-1]リスト5 Mainクラス(Main.java) [クリックでリストをテキスト表示]
 Mainクラスで使用しているEJB3StandaloneBootstrapクラスは,JBoss Embeddable EJB3.0に特有のクラスです。EJB3.0の標準に含まれるクラスではないことに注意してください。EJB3StandaloneBootstrapクラスのbootメソッドやscanClasspathメソッドは,confフォルダ以下の設定ファイルの情報を使ってEJB実行環境とコンポーネントのセットアップを行います。shutdownメソッドはそれらを破棄する処理を行います。
 このサンプルのポイントは三つです。
(1)HelloBeanクラスに@Statelessを注釈する
(2)HelloBeanクラスはHelloインタフェースを実装する
(3)MainクラスはInitialContextクラスのlookupメソッドを使ってHelloインタフェースの参照を取得する。
 @Statelessというアノテーションを使ってHelloBeanがステートレス・セッションBeanというEJBのコンポーネントであることを示します。ステートレス・セッションBeanとは,メソッド呼び出しにまたがって状態を保持しないコンポーネントです。パフォーマンスに優れていて設計がしやすいという特徴があります。ステートレス・セッションBeanはインタフェースを実装する必要があります。このサンプルではHelloBeanクラスはHelloインタフェースを実装しています。
 MainクラスはHelloインタフェースの参照をjavax.naming.InitialContextクラスのlookupメソッドを使って取得します*6。これはJNDIルックアップと呼ばれ,EJBコンポーネントの参照を取得するための基本的な方法です*7。Mainクラスのようにコンテナに管理されていないクラスからコンポーネントの参照を取得するにはJNDIルックアップを使う必要があります。
[size=-1]図5 ステートレス・セッションBeanであるHelloBeanを実行した結果
 Mainクラスを実行するとEclipseのコンソールに「Hello!」と表示されます*8図5)。MainクラスからHelloインタフェースを介してHelloBeanクラスが実行されたことがわかると思います。たったこれだけですが,これでステートレス・セッションBeanを実行したことになります。

@EJBのアノテーションでDIを利用する 次にもう少しEJB 3.0らしい機能を使ったコードを説明します。ここでは,EJB 3.0の特徴の説明で触れたDI(依存性の注入)という機能を使ってみます。この機能は,コンテナ管理されたコンポーネントから別のコンポーネントを呼び出す場合に使います。
 まずtry-ejb3プロジェクトのstep1パッケージに新しくWorldインタフェース(リスト6),WorldBeanクラス(リスト7)をそれぞれ作成します。次に,先ほど作成したHelloBeanクラスを少し変更します(リスト8)。HelloインタフェースとMainクラスには変更を加えずそのまま使用します。実行するとEclipseのコンソールに「Hello World!」と表示されます(図6)。
[size=-1]リスト6 Worldインタフェース(World.java) [クリックでリストをテキスト表示]


[size=-1]リスト7 WorldBeanクラス(WorldBean.java) [クリックでリストをテキスト表示]


[size=-1]リスト8 変更したHelloBeanクラス(HelloBean.java) [クリックでリストをテキスト表示]


[size=-1]図6 DIを利用してWorldBeanを呼び出して実行した結果
 このサンプルのポイントは二つです。
(1)WorldBeanクラスに@Statelessを指定する
(2)HelloBeanのインスタンス変数worldに@EJBを指定する
 着目したいのは,コードでは,誰もHelloBeanインスタンスにWorldインタフェースの参照を設定していないことです。HelloBeanはsetWorldメソッドを持ちますが,このメソッドは誰からも呼び出されていません。実際にはこのメソッドがなくても動作します*9。それにもかかわらず,HelloBeanインスタンスはWorldインタフェースを介してWorldBeanのgetNameメソッドを問題なく呼び出せているのです。
 一見不思議に思えるかもしれませんが,これを可能にしているのが@EJBというアノテーションです。@EJBをHelloBeanのインスタンス変数worldに注釈することで,コンテナによって依存性が注入されているのです*10
 DIが行われる際のイメージを図7に示します。一番大きな四角がコンテナを表しています。HelloBeanとWorldBeanのインスタンスはともにコンテナに管理され,インタフェースを介してのみアクセス可能です。HelloBeanには@EJBのアノテーションでWorldインタフェースへの参照が必要なことが記述されています。コンテナはHelloBeanに@EJBが注釈されていることを認識し,HelloBeanインスタンスのsayHelloメソッドが実行されるまでに必ずWorldインタフェースの参照をHelloBeanインスタンスに設定します。このためHelloBeanインスタンスはWorldインタフェースの参照に対して問題なくメソッドを実行できます。
[size=-1]図7 EJB 3.0でDI(依存性の注入)が行われる仕組み [画像のクリックで拡大表示]
 DIの利点は二つあります。まず,呼び出し元に呼び出し先の参照を取得するコードが不要になり,コードがシンプルになります。このことは,HelloBeanクラスのコード(リスト8)を見ればすぐに納得できると思います。HelloBeanクラスにはWorldインタフェースの参照を取得するためのコードはどこにもありません。
 二つ目の利点は,クラス同士の依存関係が減るので単体テストがしやすくなることです。DIを使わないことで単体テストが行いにくくなるケース,具体的には,依存関係が多いケースを考えてみるとイメージしやすいと思います。例えば,HelloBeanがServiceLocatorという別のクラスを呼び出してWorldインタフェースの参照を取得しているとします。この場合,HelloBeanのテストを行う際には,ServiceLocatorクラスも考慮してテストしなければならないので手間がかかります。一方DIを使うと,ServiceLocatorのようなクラスをそもそも使わなくて済むので,そのぶんテストが行いやすくなるのです。
 今回は,EJB 3.0について二つのことを説明しました。
(1)EJBのコンポーネントは通常のJavaオブジェクトとアノテーションを使って作成できる
(2)@EJBというアノテーションを使うことでEJBのコンポーネントを簡単に呼び出せる
 次回は主にEJB 3.0のセッションBeanについて詳しく紹介する予定です。

EJB1.rar

2.52 KB, 下载次数: 0

サンプル・プログラム

回复 支持 反对

使用道具 举报

 楼主| 发表于 2007-5-25 13:39:10 | 显示全部楼层
インターセプタの利用とトランザクション制御の容易さを体感


 前回は簡単なEJBコンポーネントの作成方法と@EJBというアノテーション(注釈)を使ったDI(DependencyInjection:依存性の注入)について説明しました。今回は,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)。
[size=-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の利用を検討するのがお勧めです。
ステートレス・セッションBeanを作成する それではステートレス・セッションBeanの作成方法を説明しましょう*1。ステートレス・セッションBeanの作成に必要なものは,通常のインタフェースとクラス,それらに記述するアノテーションです*2。EJBでは,EJBコンポーンネントのインタフェースとクラスはそれぞれ「ビジネス・インタフェース」「Beanクラス」と呼ばれます。
 ここではサンプル・コードを用いてステートレス・セッションBeanを作成し,動かしてみます。さらに,EJB3.0から導入されたインターセプタを適用する方法も合わせて紹介します。インターセプタを使うと,あるクラスのメソッド呼び出しを横取り(インターセプト)して,インターセプトしたメソッドの前後に任意の処理を割り込ませて実行させることができます*3
●インターセプタを作る まずインターセプタから作成します。インターセプタはステートレス・セッションBeanに必須ではありません。ただ,インターセプタを使用することで,Beanクラスのビジネス・メソッド(ビジネス・インタフェースを実装したメソッド)の呼び出しをすべてインターセプトできますので,メソッドのトレースや監査ログの出力などに役立ちます。また,システム運用中に,短期間でのみ必要なロジックを,既存のコードを一切変更せずに一時的に挿入するのにも有効でしょう。
 インターセプタを作成するには二つの方法があります。一つはBeanクラス内にインターセプト用のメソッドを作成する方法。もう一つはインターセプタ・クラスをBeanクラスとは別に作成し,そのクラスにインターセプト用のメソッドを実装する方法です。インターセプトのコードを複数のBeanクラスに適用できるようにするために,ここでは後者の方法を採用します。今回は,ビジネス・メソッドの開始と終了をトレースするインターセプタを作成します。
(1)インターセプタ・クラスを作成 インターセプタ・クラスにはpublicな引数なしコンストラクタが必要です。ここではTraceInterceptorという名前のインターセプタ・クラスを作成します(リスト1)。デフォルト・コンストラクタを使用するので,コンストラクタを明示的に作成していません。
[size=-1]リスト1 インターセプタ・クラスであるTranceInterceptorクラス [クリックでリストをテキスト表示]

(2)インターセプタ・クラスに@AroundInvokeを注釈したメソッドを作成 インターセプトを実行するメソッドに@AroundInvokeを注釈します。このときのメソッドのシグニチャは次の規約に従う必要があります。
public Object <METHOD>(InvocationContext) throws Exception <METHOD>はメソッド名を任意の名称にできることを表しています。サンプル・コードでは,method名をtraceとしています。InvocationContextインスタンスにはインターセプトされたBeanの情報が格納されます。InvocationContextクラスで最も重要なメソッドはproceedです。これを実行するとインターセプトしたメソッドが実際に実行されます。
 インターセプタ・クラスの作成は以上で終了です。あとでこのインターセプタ・クラスをBeanクラスに適用します。
●ビジネス・インタフェースを作る 次にステートレス・セッションBeanのビジネス・インタフェースを作成します(リスト2)。ビジネス・インタフェースでは,呼び出し元プログラムから実行可能なメソッドを定義します。
 ビジネス・インタフェースには@Localや@Remoteというアノテーションを指定できます。これらのアノテーションは,セッションBeanが「ローカル呼び出しでのみ実行されるのか,リモート呼び出しで実行可能なのか」を示します。ローカル呼び出しを行う場合は,呼び出しプログラムとセッションBeanは同一のJava仮想マシン(JVM)内に存在する必要があります。リモート呼び出しを行う場合は,呼び出しプログラムはネットワーク経由でセッションBeanにアクセスできます。サンプル・コードではローカル呼び出しを利用するため@Localを指定しています*4
[size=-1]リスト2 ステートレス・セッションBeanのビジネス・インタフェースであるHelloインタフェース [クリックでリストをテキスト表示]
●Beanクラスを作る 続いてBeanクラスを作成します(リスト3)。Beanクラスはいくつかの規約に従わなければなりません。代表的なものを以下に挙げます。
(1)クラスのアクセス修飾子はpublicでなければならない。クラスはfinalやabstractでなく,トップ・レベルのクラスでなければならない
(2)コンテナが生成できるように,引数なしのpublicなコンストラクタを持たなければならない
(3)ローカル呼び出しまたはリモート呼び出しで実行される場合,ビジネス・インタフェースを実装しなければならない
[size=-1]リスト3 BeanクラスであるHelloBeanクラス [クリックでリストをテキスト表示]
 Beanクラスには,ステートレス・セッションBeanであることを示すために@Statelessというアノテーションを指定します。また,先ほど説明したように,ステートレス・セッションBeanは対話処理の状態を維持しないので,ビジネス・メソッドでBeanクラスのフィールドを変更しないよう注意します。
 このBeanクラスにインターセプタを適用することを示すには,@Interceptorsというアノテーションを使います。@Interceptorsには,先ほど作成したTraceInterceptorクラスを指定します。@Interceptorsには複数のインターセプタ・クラスを指定できます。
●実行クラスを作る 最後にHelloBeanを実行するクラスを作成します(リスト4)。MainクラスをEclipseで実行すると,HelloBeanで出力される文字列に加えてTraceInterceptorクラスで出力されるトレース情報がEclipseのコンソールに表示されます(図2)。この結果から,HelloBeanクラスのsayHelloメソッドの前後でTraceInterceptorクラスのtraceメソッドが実行されていることを確認できます。
[size=-1]リスト4 HelloBeanを実行するMainクラス [クリックでリストをテキスト表示]


[size=-1]図2 Mainクラスの実行結果。HelloBeanが出力する文字列に加え,TraceInterceptorが出力するトレース情報も表示される


シンプルなコードで実現できる宣言的トランザクション 今度は宣言的トランザクションについて見ていきましょう。宣言的トランザクションの利点は単純です。トランザクションを制御するコードをプログラム中に記述する必要がないため,コードがシンプルになることです。
 宣言的トランザクションとは異なるトランザクション制御の方法としては,プログラムによる明示的なトランザクションがあります。プログラムによる明示的なトランザクションにはいくつか種類があります。今回は,JDBC APIを使った明示的なトランザクションとEJB3.0の宣言的トランザクションを比較してみましょう。
 JDBC APIを使ったトランザクションでは,トランザクションを完了するためにjava.sql.Connectionオブジェクトのcommitメソッドやrollbackメソッドの呼び出しが必要です(図3)。また,複数のメソッドにまたがったデータ・アクセスを一つのトランザクションで実行するには,メソッド間でConnectionオブジェクトを受け渡さなければなりません。
[size=-1]図3 JDBC APIを使ったトランザクション [画像のクリックで拡大表示]
 一方,宣言的トランザクションを使用した場合,コードはすっきりしたものになります(図4)。プログラムの中にトランザクションを制御するコードを記述する必要はありません。また,複数のメソッドにまたがったデータ・アクセスを一つのトランザクションで行うためにConnectionのようなオブジェクトをメソッド間で受け渡す必要もありません。
[size=-1]図4 EJB 3.0の宣言的トランザクション [画像のクリックで拡大表示]
 JDBCAPIを使ったトランザクション制御と異なる点は「MyLogicクラスとMyDaoクラスがセッションBeanであること」と「@TransactionManagementと@TransactionAttributeの二つのアノテーションを使用してトランザクションの振る舞いを指定していること」です。実は図4ではこれら二つのアノテーションの記述は省略できます。EJB3.0では適切なデフォルト値が用意されているため,そのデフォルト値を使用する場合はアノテーションの記述が不要なのです。
 @TransactionManagement(CONTAINER)は「トランザクション管理をコンテナが行うこと」を示します。また,@TransactionAttribute(REQUIRED)は「Beanのメソッド呼び出し時にトランザクションが実行されていればメソッドの実行をそのトランザクション内で行い,トランザクションが実行されていなければ新しくトランザクションを開始すること」を示します。図4の例では,MyDaoのメソッドは既存のトランザクション内で実行され,MyLogicのメソッドは新しく開始されたトランザクションで実行されています。これら二つのアノテーションはいずれもBeanクラスのデフォルト値なので,省略できるのです。通常は,デフォルトの値を変更したいときにのみ,これらのアノテーションを記述します。
面倒なトランザクションをコンテナが一手に引き受ける 宣言的トランザクションは,EJBコンテナがEJBコンポーネントに提供する代表的なサービスです。EJBコンポーネントはEJBコンテナに管理されます。この関係によりEJBコンポーネントがほかのプログラムから呼び出されたとき,EJBコンテナが呼び出されたメソッドをインターセプトできます*5。インターセプトにより,EJBコンテナはトランザクション処理をEJBコンポーンネントのメソッドの実行前後に割り込ませることができるのです(図5)。
[size=-1]図5 EJBコンテナがインターセプトを利用してトランザクション処理を行う仕組み [画像のクリックで拡大表示]
 EJBコンポーネントを呼び出すプログラムは,EJBコンポーネントのインスタンスを直接参照して呼び出しているわけではありません。EJBコンテナによって実装は異なりますが,呼び出し元プログラムとEJBコンポーネントのインスタンスの間には「EJBコンテナによって生成されるオブジェクト」が存在します。このオブジェクトが,EJBコンポーネントのメソッド実行の前後で,JavaEEのAPIを使ってトランザクションの開始や終了などを行っているのです。このとき,呼び出し元も呼び出されるEJBコンポーネントもインターセプトが行われることを意識する必要がありません。EJBコンポーネントにトランザクション制御のコードが不要なのはこのためです。
 EJBコンテナによるインターセプトによって実行される処理は,トランザクション処理以外にセキュリティ・チェック,非同期処理,リモート・アクセス,スレッド制御などがあります。これらの処理は特定のアプリケーションに依存しない汎用的な処理です。このような処理がEJBコンテナによってEJBコンポーネントに適用されることがEJBの特徴です。
サンプル・コードを使って宣言的トランザクションを実感する では,先ほどのサンプルを基に,宣言的トランザクションを利用するステートレス・セッションBeanを作成してみましょう。
 TraceInterceptorクラス(リスト1),Helloインタフェース(リスト2),Mainクラス(リスト4)は変更せずにそのまま利用します。リスト3のHelloBeanクラスでは文字列をコンソールに表示していましたが,今度は文字列をデータベースに保存して宣言トランザクションの効果を確かめてみます。
 データベースには,Embeddable EJB 3.0に含まれており簡単に実行できるHSQLDBを使用します。HSQLDBにはJDBC APIを使用してアクセスします。データベースに関する設定は図6を参照してください。
[size=-1]図6 サンプルを動かすためのデータベースの設定 [画像のクリックで拡大表示]

●データベースにアクセスするステートレス・セッションBeanの作成 まず,MESSAGEテーブルにデータを格納するステートレス・セッションBeanを作成します。ビジネス・インタフェースはMessageDao(リスト5),BeanクラスはMessageDaoBean(リスト6)です。DaoとはData Access Objectの略で,データ・アクセスを行うインタフェースまたはクラスであることを表しています。
[size=-1]リスト5 ビジネス・インタフェースであるMessageDaoインタフェース
[クリックでリストをテキスト表示]


[size=-1]リスト6 BeanクラスであるMessageDaoBeanクラス [クリックでリストをテキスト表示]
 MessageDaoBeanはステートレス・セッションBeanとして作成します。また,リスト1のTraceInterceptorクラスをMessageDaoBeanにも適用します。
 MessageDaoBeanではJDBC APIを利用してMESSAGEテーブルに文字列をINSERTします。JDBCAPIを利用するにはjavax.sql.DataSourceオブジェクトが必要ですが,DataSourceオブジェクトは@Resourceというアノテーションを利用してDIを行うことが可能です*6。Embeddable EJB 3.0の設定ファイルであるembedded-jboss-beans.xmlには,DataSourceが「java:/DefaultDS」という名前で登録されているので,今回はこれをそのまま利用することにします。
 MessageDaoBeanでは,DataSourceからConnectionオブジェクトを取得してデータ・アクセスを行います。Connectionオブジェクトに対してトランザクションを開始したり終了したりする処理を一切行っていないことに注目してください。
 MessageDaoBeanにはトランザクションに関するアノテーションを指定していませんが,デフォルトの値が適用されるため,insertメソッドはトランザクション内で実行されます。つまり,insertメソッドは呼び出し元のHelloBeanと同じトランザクション内で実行されることになります。
●HelloBeanクラスの修正
 次にコンソールに文字列を出力していたHelloBeanのコード(リスト3)を修正し,MessageDaoのinsertメソッドに文字列を渡すよう変更します(リスト7)。HelloBeanにはMessageDaoのDIが必要なので,HelloBeanのMessageDao型のインスタンス変数に@EJBを指定します*7
[size=-1]リスト7 MessageDaoのinsertメソッドに文字列を渡すよう変更したHelloBeanクラス [クリックでリストをテキスト表示]
 MessageDaoBean同様HelloBeanにはトランザクションに関するアノテーションを指定していませんが,デフォルトの値が適用されるので,sayHelloメソッドはトランザクション内で実行されます。このサンプルでは,sayHelloメソッドの実行前にトランザクションが開始され,実行後にトランザクションが終了します。
●呼び出しプログラムの実行 HSQLDBのServerが実行中であることを確認し,MainクラスをEclipseで実行します。するとTraceInterceptorからの出力文字列がEclipseのコンソールに表示されます(図7)。さらに,INSERT文でデータベースにデータが挿入されます。HSQLDBのGUIツールであるDatabaseManagerから「SELECT* FROMMESSAGE」というSQLを実行してみてください。サンプル・コードで実行したINSERT文がデータベースに反映されていることを確認できます(図8)。
[size=-1]図7 Mainクラスの実行結果。TraceInterceptorが出力した文字列が表示されている


[size=-1]図8 サンプルの実行結果がデータベースに反映されていることを確認。DatabaseManagerでSELECT文を実行する
●ロールバックを実行する方法 このサンプルが正常に実行されると,宣言的トランザクションによりコミットが行われます。では,ロールバックを行いたい場合にはどうすればいいのでしょうか*8。それには二つの方法があります。「javax.ejb.SessionContextオブジェクトのsetRollbackOnlyメソッドを利用する方法」と「例外を利用する方法」です。ここでは,前者の方法を紹介します。SessionContextオブジェクトはDataSource同様,@Resourceというアノテーションを使ってDIできます。
 トランザクション内でSessionContextオブジェクトのsetRollbackOnlyメソッドを実行すると,そのトランザクションはロールバックされます。HelloBeanにSessionContextオブジェクトをDIしてsayHelloメソッドの最後でSessionContextオブジェクトのsetRollbackOnlyメソッドを呼び出してみてください。データベースに対するINSERT文は実行されますが,ロールバックされるので,データベースにデータは書き込まれなくなります。
☆     ☆     ☆
 今回はステートレス・セッションBeanと宣言的トランザクションの利用法を説明しました。サンプルでは,わかりやすさを優先してJDBCAPIを使ってデータベースにアクセスしましたが,JDBC APIの代わりにJava PersistenceAPIを使うこともできます。Java Persistence APIは,EJB 3.0が含まれるJava Platform,Enterprise Edition 5(Java EE 5)におけるO/Rマッピングの標準APIです。連載の最終回となる次回は,JavaPersistence APIの使い方を紹介します。

EJB2.rar

3.04 KB, 下载次数: 0

サンプル・プログラム

回复 支持 反对

使用道具 举报

 楼主| 发表于 2007-5-25 13:42:04 | 显示全部楼层
Java標準のO/Rマッピング機能「Java Persistence API」

 前回はEJB3.0のインターセプタ機能を使ったセッションBeanとコンテナ管理によるトランザクションについて説明しました。今回はJavaEE(Enterprise Edition)やJava SE(Standard Edition)におけるO/Rマッピングの標準になる「JavaPersistence API」*1を紹介しましょう。
 Java PersistenceAPIを使うメリットは大きく二つあります。一つ目は,少ないプログラム・コードでデータにアクセスできることです。Java PersistenceAPIは,Javaオブジェクトをデータベースに格納したり,データベースのデータをJavaオブジェクトへ変換したりする処理を自動化してくれます。したがって,データベース・アクセス用のAPIであるJDBCを直接使ったアプリケーションよりも少ないプログラム・コードでデータベースへのアクセスを実現できます。
 二つ目のメリットは,移植性の高いアプリケーションを作成できることです。通常,使用するデータベースが異なると,製品固有のSQLに依存したコードがネックになり移植が難しくなります。Java PersistenceAPIはデータベース製品間の違いの多くを吸収してくれるため,高い移植性を保てます。
 Java PersistenceAPIを理解するには,まず主要な登場人物を覚えることが重要です。「エンティティ」「エンティティ・マネージャ」「永続コンテキスト」「EJBQL」の四つです。エンティティとはデータベースに永続化可能なJavaオブジェクトです。エンティティのクラスは通常のJavaクラスにアノテーション(注釈)や設定ファイルでエンティティであることを示して作成します*2。エンティティ・マネージャはJava PersistenceAPIにより提供されるインタフェースで,エンティティの取得や永続化を行うAPIを持ちます。永続コンテキストはメモリー上のエンティティの集合です。これはJava Persistence APIの実装によって管理されます。EJBQLはエンティティを取得するための問い合わせ言語です。記述方法はSQLに似ていますが,特定のデータベースに依存しません。
 Java Persistence APIを利用したアプリケーションは図1のようになります。Java EEとJava SEのどちらでも基本的に構成は変わりません。
[size=-1]図1 Java Persistence APIを利用するアプリケーションのイメージ [画像のクリックで拡大表示]

Java Persistence APIに特有の考え方に注意しよう Java Persistence APIには多くの特徴があります。ただし,JDBC APIを直接使用する場合やJDBCAPIをベースにしたフレームワークを使用する場合にはあまり一般的でない考え方も含まれているので注意が必要です。Java PersistenceAPIの特徴の中で,特に注意すべきものとしては,次の三つが挙げられます。
(1)アノテーションを使ったJavaクラスとテーブルのマッピング Javaクラスはデータベースのテーブルに,Javaクラスのインスタンス変数はテーブルのカラムにそれぞれマッピングされます。マッピング情報の詳細はアノテーションで示すことができます。マッピングの仕方は様々です。複数のテーブルを一つのクラスにマッピングしたり,一つのテーブルを複数のクラスにマッピングしたりできます。最もシンプルなマッピングは,クラスとテーブルを一対一にすることです。この場合,エンティティ・クラスのインスタンス一つがテーブルのレコード1件に対応することになります。マッピングに関する代表的なアノテーションには,@Table,@Column,@Idなどがあります。
 テーブルのマッピングだけでなくデータベース上のリレーションシップをアノテーションで示すこともできます。リレーションシップを表す代表的なアノテーションには,エンティティ間の多対1を表す@ManyToOne,1対多を表す@OneToManyなどがあります。データベースのリレーションシップをJavaのエンティティの定義に持ち込むことにより,Javaプログラムでオブジェクトのグラフ構造*3をたどってエンティティのデータにアクセスできるようになります。
 JDBC APIを直接使っても,クラスとテーブルのマッピングをプログラム・コードで記述できますが,煩雑なコーディングが必要になり,生産性や保守性の低下につながります。
(2)エンティティの四つのライフサイクル エンティティには,四つの状態からなるライフサイクルがあります。「新規の(new)」「管理された(managed)」「分離された(detached)」「削除された(removed)」です。これらの状態によってエンティティに対して行える操作が限られるので注意してください。
 Java PersistenceAPIでは,エンティティは概念的に「永続コンテキスト」という場で管理されます。上記の四つの状態は,永続コンテキストからみてどういう状態であるかを表しています。例えば「管理された」とは「エンティティが永続コンテキストで管理されている」状態を示します。「データベースに存在している」という意味ではありません。同様に,「削除された」とは「エンティティが永続コンテキストから削除された」という状態であり,データベースから削除されたという意味ではありません。
(3)トランザクションのコミット時に行われる同期化 データベースの永続化や更新や削除は,エンティティ・マネージャというJava PersistenceAPIが提供するオブジェクトが行います。一見,エンティティ・マネージャの更新系メソッドを実行すれば即座にINSERTやUPDATE,DELETEなどのSQLが発行されるように思えますが,そうではありません。エンティティの状態は,トランザクションのコミット時にデータベースに同期化されます*4。例えば,トランザクション内で同一のエンティティの値が2度以上変更されても,データベースに変更を反映するためのUPDATE文は1回しか実行する必要がないからです。このような仕組みを採用することで,データベースへのアクセスを極力減らすようになっています。
EJB 3.0/JPAの実行環境とサンプル・コードを用意する
 ではサンプル・コードを動かしてエンティティやエンティティ・マネージャの使い方を確認してみましょう。
 連載1回目2回目のサンプルでは米JBossが開発したEJBの実装であるEmbeddable EJB 3.0のalpha 3というバージョンを使用しましたが,今回は原稿執筆時点(2006年2月下旬)で最新版のalpha 5 を使用します*5。最新のEJB3.0のドラフト仕様(Proposed Final)ではJava Persistence APIの部分を中心に変更が加えられましたが,alpha 5はその変更に対応しているからです。Embeddable EJB 3.0 alpha 5はJBossのサイトからダウンロードできます。ダウンロード後,以下の手順に従って環境を整えてください。データベースには前回と同様に,Embeddable EJB 3.0に含まれているHSQLDBを使用します。
(1)Eclipseのプロジェクト下に設定ファイルを配置します。使用するEmbeddable EJB 3.0のバージョンは異なりますが,設定方法は連載1回目と同じです。
・ダウンロードしたアーカイブから,conf,lib,docs\embedded-tutorial\simple-deployment\src\resources三つのフォルダをEclipseのプロジェクト直下にコピーする
・conf,resourceフォルダをソース・フォルダとし,libフォルダ内のjarファイルすべてにビルドパスを通す
・confフォルダ内にjndi.properties(リスト1)を作成する
(2)confフォルダ内のembedded-jboss-beans.xmlのHSQLDBのURLを変更します(リスト2)。
(3)サンプルで使用するEMPLOYEEテーブルをHSQLDBに作成します。EMPLOYEEテーブルを作成するためのSQL文(DDL)をリスト3に示しました。HSQLDBのサーバーやHSQLDB のDatabaseManagerの起動方法については連載2回目を参照してください。
(4)resource/META-INFフォルダ内のpersistence.xmlを開きHibernateに関する設定を変更します(リスト4)。
[size=-1]リスト1 必要な設定ファイルであるjndi.properties [クリックでリストをテキスト表示]

[size=-1]リスト2 embedded-jboss-beans.xmlの変更個所 [クリックでリストをテキスト表示]

[size=-1]リスト3 EMPLOYEEテーブルを作成するためのSQL文(DDL) [クリックでリストをテキスト表示]

[size=-1]リスト4 persistence.xmlの変更個所 [クリックでリストをテキスト表示]

 persistence.xmlはJava PersistenceAPIで定められた設定ファイルです。persistence.xmlでは,エンティティ・マネージャの設定やエンティティ・クラスの指定,JavaPersistence APIの実装に対する設定などを行います。
 リスト4では,Embeddable EJB 3.0がJava Persistence APIの実装として利用しているHibernateの設定を変更しています*6。JavaPersistenceAPIの実装に対する設定はproperties要素を利用して行います。HibernateにはエンティティからDDLを作成し発行する機能がありますが,今回は使用しないので無効にします。hibernate.hbm2ddl.autoプロパティにvalue="none"を指定してください。また,Hibernateには自動生成するSQLをログ出力する機能があります。Java PersistenceAPIの挙動を理解するために有効なので,今回はこれを利用します。hibernate.show_sqlプロパティにvalue="true"を設定してください。
 次にサンプル・コードを作成します。サンプルではエンティティやエンティティ・マネージャの基本的な使い方を確認します。用意するのはEmployeeクラス(リスト5),エンティティ・マネージャを利用するEmployeeDaoBeanクラス(リスト6)とそのビジネス・インタフェース(リスト7),そしてmainメソッドをもつMainクラス(リスト8)です。これらのクラスとインタフェースをstep3というパッケージに作成してください。このサンプルが行うのは,Employeeエンティティの追加/更新/削除とEJB QLを使ったEmployeeエンティティの全件取得です。
 サンプルを動かすにはHSQLDBのサーバーが稼働している環境でMainクラスを実行します*7。するとEclipseのコンソールに図2のような出力が表示され,Employeeエンティティの追加,更新,削除が行われていることを確認できます。図2では省略していますが,実際にはHibernateが生成するSQL文のログも表示されるはずです。EmployeeDaoBeanクラスで行っている処理とそれに対応するSQL文を見比べてみると,Java Persistence APIの挙動をイメージできます。
[size=-1]図2 サンプルを動作させたときにEclipseの
コンソールに表示される結果
(Hibernateが発行するSQLのログは省略)
エンティティのアノテーションは指定する場所に注意 では,サンプルを詳しく説明しましょう。まず,エンティティの作成方法です。Employeeクラス(リスト5)はEMPLOYEEテーブルにマッピングされるエンティティ・クラスです。エンティティのクラスには@Entityというアノテーションを注釈する必要があります。そのほか次の規約に従う必要があります。
・引数なしのコンストラクタを持つこと
・クラスはfinalでなくトップレベルであること
・メソッドやフィールドがfinalではないこと
 また,必須ではありませんが,エンティティが値渡しされる場合(リモート・インタフェースを介して利用される場合など),そのエンティティ・クラスはjava.io.Serializableインタフェースを実装する必要があります*8。今回のサンプルではそのような使い方をしないので,このインタフェースは実装していません。
[size=-1]リスト5 EMPLOYEEテーブルにマッピングされるエンティティ・クラスであるEmployeeクラス
[クリックでリストをテキスト表示]
 エンティティと同じ名前のテーブル名がある場合は,それらはデフォルトでマッピングされます。同様にインスタンス変数名とカラム名も,同じ名前ならマッピングされます。名称が異なるときは@Tableや@Columnといったアノテーションを使って任意の名称にマッピングすることができます。以下に@Tableや@Columnを使った例を示します。
 @Entity
 @Table(name="EMP")
 public class Employee {

  @Column(name="EMP_NAME")
  private String name;
 このコードでは,EmployeeクラスがEMPテーブルに,インスタンス変数nameがEMP_NAMEカラムにマッピングされます。
 このほか,リスト5では「@Id」と「@GeneratedValue」というアノテーションも利用しています。@Idはプライマリ・キーに対応するインスタンス変数に指定します*9。@GeneratedValueはプライマリ・キーを生成する方法を指定するのに使用します。例えば,数値を自動生成する「シーケンス」というデータベースの仕組みを指定できます。この場合,@GeneratedValue(strategy=SEQUENCE,generator="CUST_SEQ")というように記述します。「strategy」でシーケンスを利用することを指定し,「generator」でデータベースのシーケンス・オブジェクトの名称を指定しています。サンプル・コードのように単に@GeneratedValueと記述した場合は,実行環境がデータベースに適切な生成方法を自動的に適用してくれます。プライマリ・キーを自動生成しないでプログラム内で明示的にセットする場合は,@GeneratedValueは不要です。リスト5では@GeneratedValueを指定しているので,データベースへのINSERT時にプライマリ・キーが自動で設定されます。
 これらのアノテーションには注意すべき点があります。指定する場所です。リスト5の@Idや@GeneratedValue,先ほどの例で@Columnを注釈している個所に注目してください。これらのアノテーションはインスタンス変数に指定しています。一方,インスタンス変数に指定する代わりにgetterメソッドに指定することもできます*10。インスタンス変数に指定した場合とgetterメソッドに指定した場合の違いは,Java PersistenceAPIの実行環境(Hibernateなど)がどのようにインスタンス変数にアクセスするかの違いになります。インスタンス変数に指定した場合,実行環境はインスタンス変数に直接アクセスします。getterメソッドに指定した場合は,実行環境はgetterメソッドを介してインスタンス変数にアクセスします。後者の場合,getterメソッドで値を返す以外の処理をしていると,実行環境が値にアクセスするたびにその処理も実行されることになるので注意してください。
 なお,インスタンス変数とgetterメソッドのどちらにアノテーションを指定したとしても,アプリケーションはメソッド経由でインスタンス変数にアクセスする必要があります。
エンティティ・マネージャでライフサイクルを制御 次にエンティティ・マネージャを利用してエンティティの取得や永続化を行うEmployeeDaoBeanクラス(リスト6)を見てください。エンティティ・マネージャは@PersistenceContextというアノテーションを使ってEmployeeDaoBeanにDI(Dependency Injection:依存性注入)できます。
[size=-1]リスト6 エンティティ・マネージャを利用するEmployeeDaoBeanクラス
[クリックでリストをテキスト表示]
 エンティティ・マネージャは,エンティティを永続化したり取得したりするメソッドを持ち,エンティティのライフサイクルを制御します。まず,エンティティのライフサイクルについて整理しましょう。エンティティのライフサイクルには四つの状態があります。
(1)新規の(new):
 「new Employee( );」のようにプログラム上で新規に生成されたエンティティの状態です。プライマリ・キーを持たず,永続コンテキストにも関連付けられていません。
(2)管理された(managed):
 プライマリ・キーを持ち,永続コンテキストに関連付けられた状態です。管理されたエンティティに対する変更は自動的に検出されトランザクションのコミット時にデータベースに反映されます。
(3)分離された(detached):
 プライマリ・キーを持ち,永続コンテキストに関連付けられていない状態です。永続コンテキストに関連付けられたエンティティは永続コンテキストが終了するとすべて分離された状態になります。永続コンテキストはデフォルトではトランザクションが終わったときに終了します。サンプル・コードではEmployeeDaoBeanのメソッド呼び出しが完了するとトランザクションが終了します。したがって,EmployeeDaoBeanからMainクラスに返されるエンティティは,すべて分離された状態です。
(4)削除された(removed):
 プライマリ・キーを持ち,永続コンテキストに関連付けられています。データベースからの削除が予定された状態です。実際の削除はトランザクションのコミット時に行われます。
 以上の説明をふまえて,EmployeeDaoBeanで利用しているエンティティ・マネージャのメソッドについてそれぞれ説明します。
 まず「createQueryメソッド」です。EJBQLを実行するのに必要なQueryインスタンスを作成します。EmployeeDaoBeanクラスのgetAllEmployeesメソッドで使われています。EmployeeDaoBeanでは「select e from Employeee」という文字列をcreateQueryメソッドに渡しています。これはEmployeeエンティティを全件返すEJBQLになります。「Employee」はテーブル名ではなくエンティティ名です。QueryインスタンスのgetResultListメソッドを実行することで,Employeeエンティティのリストを戻すことができます。EJB QLで返されるエンティティは「管理された」エンティティです。
 EJB QLの文法は一般的なSQLによく似ています。条件や並べ替えの指定もSQLに似た形式で使用できます。例えば次のようなEJB QLを実行できます。
select e from Employee e where e.salary > 100000 order by e.name 次が「findメソッド」です。このメソッドはプライマリ・キーによる検索を行います。EmployeeDaoBeanクラスのupdateSalaryメソッドとremoveメソッドで使われています。第1引数で戻り値の型を指定できます。findメソッドで返されるエンティティは「管理された」エンティティです。
 「mergeメソッド」は,「分離された」エンティティを永続コンテキストにマージして「管理された」エンティティを生成します。EmployeeDaoBeanクラスのupdateメソッドで使われています。「persistメソッド」は,「新規の」エンティティを永続化し,エンティティを「管理された」状態にします。EmployeeDaoBeanクラスのcreateメソッドで使われています。「removeメソッド」は,エンティティを削除しエンティティを「削除された」状態にします。EmployeeDaoBeanクラスのremoveメソッドで使われています。
 ここで,「管理された」エンティティと「分離された」エンティティについて,もう少し詳しく見てみましょう。
 EmployeeDaoBeanクラスのupdateSalaryメソッドを見てください。このメソッドでは,エンティティ・マネージャのfindメソッドにより「管理された」エンティティを取得し,salaryを変更しています。updateSalaryメソッドで行っているのはこれだけです。変更したエンティティをエンティティ・マネージャのメソッドに渡すといった処理はしていません。それにもかかわらず,このコードによりデータベースへの更新が行われます。なぜならば,「管理された」エンティティの変更は自動的に検出されるからです。
 次にEmployeeDaoBeanクラスのcreateメソッドを見てください。このメソッドは,エンティティ・マネージャのpersistメソッドを使ってエンティティを「管理された」状態にしてから呼び出し元に返しています(この時点でデータベースへのINSERTが行われます)。しかし,呼び出し元のMainクラスがこのエンティティを受け取ったときには,エンティティはすでに「分離された」状態になっていることに注意してください。トランザクションと同時に永続コンテキストが終了しているためです。Mainクラスは「分離された」エンティティに対して変更を行いますが,これだけではデータベースへ変更は伝わりません。この変更をデータベースへ反映させるには,エンティティ・マネージャのmergeメソッドを利用する必要があります。EmployeeDaoBeanクラスのupdateメソッドを見てください。EmployeeDaoBeanクラスのupdateメソッドでは「分離された」エンティティをエンティティ・マネージャのmergeメソッドに渡し,新しく「管理された」エンティティを生成しています。サンプル・コードではmergeメソッドが「管理された」エンティティを返すことをわかりやすく示すために
Employee managed =
 entityManager.merge(employee);
としています*11
 EmployeeDaoBeanクラスのupdateメソッドでは,トランザクションのコミット時に「管理された」エンティティとデータベースの実際のデータが比較され,自動的に適切な更新が行われます。
 エンティティのライフサイクルとエンティティ・マネージャのメソッドの関係を図3にまとめました。
[size=-1]図3 エンティティのライフサイクル

[size=-1]リスト7 ビジネス・インタフェースであるEmployeeDaoインタフェース
[クリックでリストをテキスト表示]

[size=-1]リスト8 mainメソッドを持つMainクラス [クリックでリストをテキスト表示]

☆     ☆     ☆
 さて,今回でEJB 3.0入門講座はおしまいです。限られたスペースでしたが,EJB 3.0について基本となるところを中心に取り上げました*12。EJB 3.0は,まだ最終的な仕様がリリースされていない新しい技術ですが,実際の開発でも有効です。このことを感じ取っていただけたでしょうか。この連載が皆さんにとってEJB 3.0に触れるきっかけになれば幸いです。

EJB03.rar

2.61 KB, 下载次数: 1

サンプル・プログラム

回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注~册

本版积分规则

小黑屋|手机版|咖啡日语

GMT+8, 2024-5-12 19:55

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表