コンテナー管理パーシスタンス (CMP) エンティティーEnterprise JavaBeansは、本質的に永続データ (一般にはリレーショナル・データベースの表の形式のデータ) のラッパーに、トランザクション制御およびセキュリティーのサポートを追加したものです。CMPエンティティーbeanは、6個以上の別々のファイルから構成されており、それらにはコンパイル時には容易に検査できない依存性があります。オブジェクト・リレーショナル・データベースのマッピングが正しくなるように特別の注意が必要であり、さらに理想的には、要件が変化した場合に必要となる変更が最小限で済むようにコードを設計するべきです。この記事では、大規模なEJBプロジェクトの作成に伴う問題点をいくつか取り上げ、IBM alphaWorksのWebサイトにあるダウンロード可能なツールEJBMakerを使うことで、そのうちのいくつかが解決されることを示します (参考文献を参照)。
EJBMakerは、XMLで書かれた1個のCMP EJB記述ファイルを読み、自動的にJavaソース・コードとデータベース・スクリプトを生成します。このツールは、beanのファインダーおよび初期化メソッドの定義のための退屈な作業を実行します。さらに、CMP EJBを相互に関連付けるリレーションシップEJBを生成する機能があります。EJBMakerは、XMLディスクリプター・ファイルの更新内容を常に把握し、変更のあったEJBのためのJavaコードだけを生成します。さらに、XMLを使用して短時間で初期化を実行したりEJBからデータを抽出したりすることを可能にするアダプター・クラスが生成されます。EJBMakerは、IBM WebSphere Application Serverで使うために最適化されてはいますが、少し手を加えれば他のアプリケーション・サーバーでも使用できます。
EJBテクノロジーの優れた点の1つは、少なくとも理論的にはEJBを1回だけ書けばEJB準拠の任意のアプリケーション・サーバーにそれを配置できる、ということです。その仕様のバージョン2.0にはEJB照会言語などの技術が含まれており、その目標にさらに近づいています。
"Write Once, Run Anywhere" (1回書いてどこででも実行) というところにはまだ到達していませんが、EJBをあるアプリケーション・サーバーから別のアプリケーション・サーバーに移植することは、驚くほど簡単です。コンテナー管理パーシスタンス (CMP) エンティティーEJBについては特にそのことが言えます。CMPエンティティーEJBを作成する開発者は、beanの状態をデータベースに永続的に保管するためのコードを書くことを心配する必要がありません。そのようなコードは、アプリケーション・サーバー上のデプロイメント段階で自動的にすべて生成されるからです。それにより、beanの柔軟性は少し低くなりはしますが、開発期間は短くなります。アプリケーション・サーバーの設計者は、EJBの仕様によって定義されている「コンテナー管理パーシスタンスのためのエンティティーbeanコンポーネント規約」を守る限り (参考文献を参照)、EJBデータを永続的に保管したり取り出したりするための任意のメソッドを使用できます。最も一般的なソリューションは、Javaデータベース・コネクティビティーAPI (JDBC) を使ってリレーショナル・データベースにデータを保管するというものです (JDBCについては参考文献を参照)。これは、特にIBM WebSphere Application ServerおよびBEA WebLogicで使われている手法です。
エンティティーEJBのもう1つの種類は、Bean管理パーシスタンス (BMP) EJBです。その場合、bean情報を永続的に取り出したり保管したりするためのコードは、開発者が自分でコーディングする必要があります。既存のシステムやファイル・システムにデータを保管する必要がある場合には、BMPが理想的です。しかし、柔軟性を高くすると移植性が低くなります。アプリケーション・サーバーがシステム・リソースへのアクセスすべてを制御していないなら、異なる複数のサーバー環境の間におけるそのようなリソースの存在や互換性については、何の保証もできなくなります。
BMPインプリメンテーションでなければならないという特定の要件がないのであれば、CMPのほうが優れています。CMP beanはどうやって書くのでしょうか? 次のファイルを作成する必要があります。
- EJBキー。キー・クラスには、そのbeanの固有基本キー・インプリメンテーションが含まれます。EJB仕様のバージョン2.0は、RMI-IIOPの中で有効な値の型であるクラスとして基本キー・クラスを定義します。
-
EJBファインダー・ヘルパー・インターフェース。このファイルには、EJBホーム・インターフェースの中で宣言されている各ファインダー・メソッドごとに、java.lang.Stringフィールドが1つずつ含まれています。ストリングは、ファインダー・メソッドでbeanインスタンスが取り出された時点で動的に実行されるSQL照会に初期化されます。
注: このファイルは、IBM WebSphere Application Serverに特有のものです。その他のアプリケーション・サーバー環境 (たとえばEnhydraやWebLogic) では、それぞれ独自の照会言語形式を使って、XMLデプロイメント・ディスクリプターの中に照会が入れられます。そのような環境の場合、このファイルは無視することができ、デプロイメント・ディスクリプターの中に手動で照会を入力することによって置き換えることができます。 - デプロイメント・ディスクリプター。デプロイメント・ディスクリプターは、EJBクラスおよびインターフェースの名前など、beanに関するメタデータと、そのbeanに関連する永続フィールドのリストが含まれている、シリアライズJavaクラスです。EJB仕様のバージョン1.1以来、デプロイメント・ディスクリプターをXMLで書くことができるようになっています (後のデプロイメント段階でシリアライズJavaオブジェクトに変換する)。
- データベース・スクリプト。beanデータを永続的に格納するのに必要なリレーショナル・データベース表がアプリケーション・サーバーによって作成されない場合、そのためのデータベース・スクリプトを作成できます。
上記のすべてのファイルが存在することを確認するだけでなく、開発者がCMPエンティティーbeansを書く際に避けなければならない落とし穴がいくつかあります。まず、コンパイル時には確認できないファイル間の依存関係が存在します。
- リモート・インターフェースで宣言されているメソッドが、EJBリモート実装クラスの中に実際に実装されていなければなりません。EJBリモート実装クラスでEJBリモート・インターフェースを直接実装する場合であれば、依存関係はコンパイル時に確認できます。しかし、リモート実装クラスのインスタンスはサーバー側に属するものなので、クライアント側オブジェクトとしてのリモート・インターフェースとの間に本質的な違いがあります。リモート実装クラスでリモート・インターフェースを実装するのであれば、そこに、リモート・インターフェースによって拡張されるインターフェース
javax.ejb.EJBObjectによって宣言されるメソッドも実装することが必要になります。このインターフェースには、サーバー側には属さないメソッドがたくさん含まれています。 - beanのEJBHomeインターフェースで宣言されるファインダー・メソッドは、多くの場合別のファイルで定義される照会に関連付けられたものでなければなりません。WebSphereの場合、EJBファインダー・ヘルパー・クラスにはEJBホーム・インターフェースで宣言されているファインダー・メソッドごとに1個ずつ静的Stringフィールドが含まれていなければならず、そのフィールドの名前はファインダー・メソッドの名前に一致していなければなりません。Enhydraでは、XMLデプロイメント・ディスクリプターに、EJBHomeなどで宣言されるファインダー・メソッドに一致するfinder-method-jdbc-mapping要素が含まれていなければなりません。
- EJBリモート実装クラスには、EJBホーム・インターフェースで宣言される各
create(...)メソッドごとに、ejbCreate(...)メソッドが含まれていなければなりません。特に、ejbCreate(EJBKey...)メソッドは常に含まれていなければなりません。これは、EJBキー・インスタンスを使ってbeanの新しいインスタンスを作成するものです。
その他の要件として、次のことがあります。
- XMLデプロイメント・ディスクリプターに、EJBコンポーネントの正しいクラス名と永続フィールドに必要なすべての宣言が含まれていることを確認してください。
- オブジェクトとリレーショナル・データベースのマッピングが正しく設定されている必要があります。それには、永続フィールドが正しい型のデータベース列にマッピングされていること、そして、アプリケーション・サーバーが自動的に生成する永続コードの中に存在する特殊な要件を満たすように表が設計されていることの確認が含まれます。たとえば、そのような要件の1つとして列の順序があります。
これらの要件すべてを読むと、CMPエンティティーbeanを書くことは並大抵のことではないと感じられるかもしれません。幸いなことに、これらのステップの多くには、便利なツールがあります。たとえば、IBM VisualAge for Java IDEには、すばらしいEJB開発環境が含まれています。必要なコードの多くが自動的に生成されるようになっており、オブジェクトとリレーショナル・データベースのマッピングのための支援機能もあります。BEA SystemsのWebGain Studio とWebGain StructureBuilder には、UMLモデルをEJBコンポーネントに自動的に変換するなど、開発者のためにEJB開発作業の一部を支援する機能があります。しかし、もう統合開発環境を変えられないという場合はどうでしょうか? ほとんどお手上げ状態か ...
CMP beanを書くことに伴うトリッキーな問題を解決するために、EJBMakerツールが作成されました。このツールの生成するファイルはWebSphere特有のものですが、その出力を他の環境用に修正することは、実にわかりやすい作業です。CMP beanに関係するすべての情報 (beanの名前、永続フィールド名およびクラス、リレーショナル・データベース・マッピングを含む) は、単一のファイルに格納されます。ファイルの形式はXMLなので、柔軟性があり、EJBMakerの今後のバージョンで拡張するのも容易です。
このEJBMaker XMLファイルから、次のものが生成されます (上の「CMP beanの解剖」を参照)。
- 各CMP beanごとのJavaソース・ファイル。これには、EJBリモート・インターフェース、EJBリモート実装クラス、EJBホーム・インターフェース、EJBキー・クラス、およびEJBファインダー・ヘルパー・インターフェースが含まれます。
- XMLデプロイメント・ディスクリプター。これには、EJBクラス名、トランザクションの永続フィールドおよびbean属性、セキュリティーと分離のレベルが含まれます。<o:p></o:p>
- データベース・スクリプト。リレーショナル・データベース中にパーシスタンス表を生成するのに使うスクリプト。これらのスクリプトはDB2用に調整されてはいますが、少し手を加えるだけで他のデータベースでも動作します。
オブジェクトとリレーショナル・データベースのマッピングも含め、すべての依存関係は自動的にチェックされます。bean中の各永続フィールドごとに値の一致するインスタンスを取り出したり、すべての既存のbeanインスタンスを取り出したりするためのデフォルトのファインダー・メソッド (getAllInstances() メソッド) が生成されます。それによりbeanは、さまざまな異なった状況で使用できる柔軟なものになります。特殊なファインダー・メソッドが必要なら、それを直接XML記述ファイルに入力することができます。
次に示すのは、EJBMaker XML形式で記述した1つのCMP beanの例です。
<?xml version="1.0"?>
<!DOCTYPE ejbmaker SYSTEM "ejbmaker.dtd">
<ejbmaker>
<!-- default attributes for beans -->
<transaction-attr>TX_REQUIRED</transaction-attr>
<isolation-level>SERIALIZABLE</isolation-level>
<run-as-mode>SPECIFIED_IDENTITY</run-as-mode>
<re-entrant>false</re-entrant>
<!-- A Simple Account CMP entity EJB -->
<bean name = "Account" type = "entity">
<persistent_field dt="int" col_dt="INTEGER">
ACCTNUMBER
</persistent_field>
<persistent_field dt="java.lang.String" col_dt="VARCHAR(13)">
SSN
</persistent_field>
<persistent_field dt="double" col_dt="DOUBLE">
BALANCE
</persistent_field>
<finder_method name="findNegativeAcct">
<sql>
SELECT * FROM EJB.ACCOUNTBEANTBL WHERE BALANCE < 0
</sql>
</finder_method>
</bean>
</ejbmaker>
|
このファイルは、いくつかの部分で構成されています。transaction-attr、isolation-level、run-as-mode、およびre-entrant は、SunのXML EJBデプロイメント・ディスクリプター形式から直接取ったものです。どのbeanについても、これらのbean属性がデフォルト属性としてコピーされ、生成されるデプロイメント・ディスクリプターの中に入れられます。
その後の最初の要素は、次のものです。
<bean name = "Account" type = "entity"> |
これは、新しいbean宣言の始まりを示しています。このbeanのJava Naming and Directory Interface (JNDI) 名はAccountであり、このbeanのタイプはentityです。現在のところ、EJBMakerでサポートするbeanタイプはentityだけです。
次の数行には、パーシスタント・フィールドの識別方法が示されています。
<persistent_field dt="int" col_dt="INTEGER">
ACCTNUMBER
</persistent_field>
|
Account beanで定義されている最初のパーシスタント・フィールドは、ACCTNUMBERです。パーシスタント・フィールドの保管に使われるJavaクラスは、基本的なintクラス (dt属性で宣言されるもの) であり、データベース列のデータ型はcol_dt属性がINTEGERであるとして宣言されています。さらに2つのパーシスタント・フィールドSSNとBALANCEが定義されています。
次の数行には、特殊なファインダー・メソッドの指定方法が示されています。
<finder_method name="findNegativeAcct">
<sql>
SELECT * FROM EJB.ACCOUNTBEANTBL WHERE BALANCE < 0
</sql>
</finder_method>
|
ファインダー・メソッドの名前はfindNegativeAcctです。要素中に入れられるSQLコードは、XMLの内容の規則に従ったものでなければならないことに注意してください。特に、このことは、< 記号などのXMLの予約文字をエスケープすることが必要であることを意味しています。
EJBMakerによってXMLファイルを渡すと、ソース・コード、XMLデプロイメント・ディスクリプター、およびデータベース・スクリプトが生成されます。
上記の例からは、次に示すJavaクラスおよびメソッドが生成されます。
-
Account.java
メソッド: getACCTNUMBER()、setACCTNUMBER(int)、getSSN()、setSSN(String)、getBalance()、setBalance(double)、getKey()、initialize(TXDocument)、getXML() -
AccountBean.java
メソッド: getACCTNUMBER()、setACCTNUMBER(int)、getSSN()、setSSN(String)、getBalance()、setBalance(double)、getKey()、initialize(TXDocument)、getXML()、ejbActivate()、ejbLoad()、ejbPassivate()、ejbStore()、ejbRemove()、setEntityContext()、unsetEntityContext() -
AccountHome.java
メソッド: create(AccountKey)、findByPrimaryKey(AccountKey)、findAllInstances()、findByACCTNUMBER(int)、findBySSN(String)、findByBALANCE(double)、findNegativeAcct() -
AccountKey.java
メソッド: AccountKey()、AccountKey(String)、equals(Object) -
AccountBeanFinderHelper
メソッドなし -
AccountXMLAdaptor
メソッド: AccountXMLAdaptor()、AccountXMLAdaptor(TXDocument)、getXML()、setACCTNUMBER(Integer)、getACCTNUMBER()、setSSN(String)、getSSN()、setBALANCE(Double)、getBALANCE()
これらのメソッドのうちのいくつかについて説明しておきます。リモート・インターフェース (Account.java)、およびリモート・インターフェースの実装 (AccountBean.java) には、getKey() というメソッドがあります。このメソッドは、単にbeanの基本キーを、そのスーパークラスのgetPrimaryKey() メソッドから戻されるAccountKeyインスタンスとしてではなく、Stringインスタンスとして戻すだけです。
リモート・インターフェースとリモート実装クラスには、さらにinitialize(TXDocument) およびgetXML() メソッドもあります。これらの2つのメソッドは、XMLを使ってbeanをすばやく初期化し、bean中の情報をXMLとして取り出すために使われます。これら2つのメソッドの使用方法については、この例をご覧ください。
このコードでは、AccountXMLAdaptor クラスの使い方も示されています。これは、Account XML文書のラッパーであり、Accountに関連するプロパティーを設定したり取得したりするためのメソッドが含まれています。Java基本クラスのパーシスタント・フィールドが、アダプター内のそれに相当する非基本クラスに変換されていることに注意してください。そのようにして、非初期化 状態を表現するのにヌル値を使うことが可能になります。これは、パーシスタント・フィールドのサブセットだけを設定する場合に便利です。Accountに関連するすべての情報は、別のクラスのgetXML() メソッドを使ってXML形式にエンコードされます。XMLの形式は明快です。ルート要素は常にEJBの名前であり、その下に各パーシスタント・フィールドごとに1つの要素があります。Accountの例の場合は、EJBインスタンスをシリアライズして、次のようなXML文書にすることもできます。
<Account>
<ACCTNR>1111</ACCTNR>
<SSN>123-45-6778</SSN>
<BALANCE>1000000</BALANCE>
</Account>
|
EJBMakerは、bean間の多対多関係を表現するのに使うEJBも生成できます。たとえば、単純なカレンダー・アプリケーションを考えてみましょう。2つのCMPエンティティーbeanを使います。1つはカレンダーのイベントについての情報を含むもの (Event)、もう1つはこのカレンダー・アプリケーションのクライアントについての情報を含むもの (Client) です。ここで、特定のカレンダー・イベントの面倒を見るクライアントを表現したいとします。1つのあまり美しくないソリューションは、その特定のイベントの処理をするすべてのクライアントの基本キーを含むEvent beanに1個のフィールドを追加することです。複数の基本キーを、たとえばコンマで区切って連結できます。あるイベントの処理をするすべてのクライアントを取り出すには、基本キーのコンマ区切り文字列を受け取り、各キーごとにClientのホーム・インターフェースの中でfindByPrimaryKey(...) を使って検索を実行するようなコードを書く必要があるでしょう。そのコードをセッションbeanの内部に書くとしても、そのパフォーマンスは、この後に示す第2のソリューションよりも遅くなるでしょう。
もっとエレガントなソリューションがあります。それは、イベントとクライアントの間の関係を表現する付加的な表を使う方法です。そのうちの2つの列には、Event EJBのインスタンスの基本キーとClient EJBのインスタンスの基本キーを入れます。そこで、1つのイベントに関係するすべてのクライアントを検索する作業は、Clientホーム・インターフェースの中に1つのファインダー・メソッドを書き、ヘルパー・ファインダー・クラスの中に1つのSQL照会を書くだけのことになります。EJBMakerが実行するのは、まさにこのことです。2つのbeanの間の関係は、次のようなテクニックを使って構成されます。
<ejbmaker> <!-- Event and Client EJB declarations go here -->
<relation name = "Attendee">
<bean_ref_1>Event</bean_ref_1>
<bean_ref_2>Client</bean_ref_2>
</relation>
</ejbmaker>
|
関係は、Attendeeという新しいCMPエンティティーbeanによって表現されています。それには2個のフィールドがあります (基本キーも含めれば3個)。それらのフィールドには、Event EJBとClient EJBの基本キー・インスタンスが含まれています。さらに、2つの付加的なファインダー・メソッドが、1つはEventホーム・インターフェースの中に、もう1つはClientホーム・インターフェースの中に作成されています。Eventホーム・インターフェース・ファインダー・メソッドは、次のとおりです。
java.util.Enumeration findByAttendee(String pkey)
pkeyはClient EJBの基本キーです。このメソッドは、特定のクライアントが処理するイベントのEnumerationを戻します。Clientホーム・インターフェースにも、これと同じメソッドがあります。
java.util.Enumeration findByAttendee(String pkey)
しかし、このクラスの場合、pkeyはEvent EJBの基本キーであり、このメソッドは特定のイベントを処理するクライアントのEnumerationを戻します。
EJBMakerによって生成される最後のコード部分は、パーシスタントEJB表を作成するためのデータベース・スクリプトです。必要なすべての表を作成するCreateTables.ddlスクリプトに加えて、各EJBごとに1つずつスクリプトが作成されます。しかし、それらの表は、仮想表 (またはビュー) が作成されるまでは、アプリケーション・サーバーで使用することはできません。それらのビューは、生成されるEJBパーシスタンス・コードによって期待される順序になるよう、それらの表の列順を変更します。EJBMakerには、CreateViews.ddlスクリプトを生成するためのツールが付属しています。このスクリプトは、EJBが配置されるたびに生成しなおす必要があります。そうしないと、列順が狂ってしまう可能性があります。アプリケーション・サーバーと物理データベース表の間でビュー抽出処理を使うことによって、EJBが再配置された場合でもパーシスタンス表の中の情報を維持することができます。
この記事では、エンティティーEJBを書く上での問題点の概略を説明し、EJBMakerツールの機能のいくつかを紹介しました。alphaWorks からこのソフトウェアをダウンロードして、実際に試してみてください。何かご意見やご感想がありましたら、ぜひお寄せください。寄せられる情報は、この製品の今後のバージョンをさらに良いものにするのに役立ちます。
-
EJBMaker はalphaWorksからダウンロードしてください。
-
EJBのダウンロードと仕様の入手はここです。
-
Javaデータベース・コネクティビティーAPI についてさらに調べてください。
-
BEA WebLogic Application Server のWebサイトもご覧ください。ダウンロードもできます。
Stefan Edlundは、IBM Almaden Research Centerのリサーチ提携/スタッフ・ソフトウェア・エンジニアです。現在、Web Technologies部に属しており、リサーチを担当し、Webベースのアクティブな個人情報管理システムを設計しています。Stefanは、StockholmのRoyal Institute of Technologyから、コンピューター・サイエンス分野でのM.S. 学位を授与されています。Stefanの連絡先は edlund@almaden.ibm.com です。