J2EEデザイン・パターンに基づくデータアクセスの実現
 - Business Components for Java - 

2002年5月

目次

はじめに
    J2EEデザイン・パターン
    J2EEデザイン・パターンのアプリケーションへの適用
J2EE に対するアプローチの比較(手動コーディングとフレームワークの使用)
    Pet Store Demo の簡単なマスター/ディティール JSP ページ
    フレームワーク開発におけるアプローチ
    BC4J を使用したPet Store Demoの実装
BC4J を使用したEJB 開発の効率化
    多くの EJB アーキテクチャの中で突出したパフォーマンス
    BC4J データアクセス・オブジェクト機能の概要
    BC4J データアクセス・オブジェクトの理解
    BC4Jを使用したEJB セッションBeanのサンプル
    EJB を利用しないローカル・モードでのデプロイ
    BC4J と組み合わせるクライアント・テクノロジ
    EJB 2.0による変更点
    まとめ
BC4J デザイン・パターン・カタログ
FAQ

はじめに

開発者は、ここ数年で独自のテクノロジを使用したクライアント/サーバー型のアプリケーション開発から、スタンダードな技術を用いた多階層アプリケーション開発へ急速に移行してきました。こうした流れを作り出している企業の多くは、標準化されたJava™2 Platform, Enterprise Edition(J2EE)を選択して、次のような機能を使用しています。

これらのJ2EEテクノロジおよびAPI群により、J2EE準拠のアプリケーション・サーバーへアプリケーションをデプロイしたり、SQL準拠のリレーショナル・データベースを使用したアプリケーションを構築するための有効な要素が提供されます。ただし、経験豊富なJ2EEの開発者が言うように、シンプルなJ2EE準拠のアプリケーションを作成する場合と、モジュール化されたスケーラブルなアプリケーションを作成する場合では、作成にかかる労力が明らかに違います。

このドキュメントでは、オラクルのJ2EE準拠のBusiness Components for Java(BC4J)フレームワークによって、上記のJ2EEテクノロジのフルセットを利用する開発ソリューションがいかに容易に実現できるかを説明します。通常は手動でコードを記述しなければならないところをBC4Jを使用することによって、デザイン・パターンを実装済の多数のコンポーネントを容易に活用できます。実際に、オラクル社内のアプリケーション開発チームの多くは、BC4Jを使用して高機能かつ高性能のJ2EEアプリケーションを短期間に開発しています。これからの章で示すように、一般的なJ2EEアプリケーションで必要なアプリケーション・プログラミング・コードの量は、見かけよりもかなり多いため、J2EEの開発でBC4Jを利用する社内および社外の開発チームが増えています。オラクルでは、J2EEプラットフォームに対して長期にわたって投資を続けています。そのため、BC4JフレームワークおよびOracle9i JDeveloperの提供する開発環境は、J2EEプラットフォームを対象とした開発者の生産性を上げるために非常に有用です。

J2EEデザイン・パターン

EJB 1.0が最初にリリースされた1998年3月から、開発者はJ2EEテクノロジを実際のアプリケーションで使用できるようにするため、多くのことを学んできました。こうした先駆者たちは自身の経験を、EJB開発者にベストプラクティスなコーディング・テクニック(J2EEデザイン・パターンと呼ばれる)としてフィードバックしています。SunのJ2EEデザイン・パターンのWebサイトでは、多くのデザイン・パターンを簡単に説明しています。このガイドラインは、EJBチュートリアルを終了したことが前提になっており、チュートリアルで習得した内容をどのような場合に適用し、どのような場合に適用しないかを示すことで、最適な結果が得られるようにしています。

習得すべきテクニックの大半は、Sunの『Designing Enterprise Applications with the Java™ 2 Platform, Enterprise Edition』(『J2EE Blueprints』シリーズの一部)、およびサードパーティの著者による類似の本にも記載されています。これらの優れた本を読むほかに、SunのJava Pet Store Demoのソース・コードを学習し、開発者がこのサンプル・アプリケーションに対してどのようにデザイン・パターンを適用しているかに着目して、理解を深めることもできます。

これからチームで開発するJ2EEアプリケーションの種類や規模によらず、Pet Store Demoには実際のアプリケーションで必要な、以下のような一般的なタスクについて説明されています。

実際に、Pet Store Demoに含まれる豊富なソース・コードには、次のような多数のJ2EEデザイン・パターンの実装例があります。

  1. Data Access Objectsパターン

    従属オブジェクトを、エンタープライズBeanとしてではなく軽量で永続的なクラスとして実装し、データ交換時の不要なオーバーヘッドを回避します。

  2. Model/View/Controller(MVC)パターン

    「アプリケーション・オブジェクト」が、アプリケーションのデータ・モデルを提示し、データを表示するレイアウト・コードと完全に分離します。

  3. Session Facadeパターン

    セッションBeanでラッピングすることによって、効率の悪いエンティティBeanのリモート・クライアント・アクセスを回避します。

  4. Transfer Object(Value Object)パターン

    クライアント・プログラムで必要な関連属性のセットをグループ化したオブジェクトを作成し、それを一度に転送することで、ネットワークの不要なラウンドトリップを回避します。

  5. Value List Handler(Page-by-Page Iterator)パターン

    大量のコレクションを表示用の単位に細分化し、不要なデータがクライアントに送信されないようにします。

  6. Fast-Lane Readerパターン

    JDBC APIに直接アクセスして、読取り専用データに対する不要なオーバーヘッドが発生しないようにします。

これらはあくまで"デザイン・パターン"であり、特別な実装方法を強制するものではありません。実装には、単純に手動でコードを記述するソリューションから、より洗練された汎用的なフレームワークを使用するソリューションまで、さまざまな方法があります。通常、それらのアプローチによって、それぞれの開発者に必要な作業量が違ってきます。学習用に作成されたサンプル・アプリケーションであるPet Store Demoでは、Webにおける単純な購買アプリケーションにおいて、パターンを実装するためのひとつの有効なアプローチについて説明しているに過ぎません。

J2EEデザイン・パターンのアプリケーションへの適用

参考書を読んだり、サンプル・コードについて学習したりすることは、開発をおこなう上で非常に有効です。実際に自分でJ2EEアプリケーションを作成する場合には、J2EEデザイン・パターンを適用するために、次の2つの方法を選択することができます。

  1. デザイン・パターンを手動で実装する

    パターン・コードが必要になるたびに、手動でコードを記述し、デバッグします。

  2. フレームワークを適用する

    一般的なデザイン・パターンを実装済みのフレームワークを適用します。

非常に単純なJava Pet Storeアプリケーションを構成している200以上のクラスを調べれば、これらのベストプラクティスなデザイン・パターンをすべて実装するには、膨大な量のプログラミング・コードを記述しなければならないことがわかります。開発チームは、このようなコードの記述やデバッグにかかる時間を最小限にして、代わりに、競合する他社とビジネスを差別化するための優れたビジネス・ロジックやユーザー・インターフェースを記述する時間を増やしたいと考えるはずです。上記のフレームワーク・ベースのアプローチは、Sunの『J2EE Blueprints』の推奨事項として、次のように記載されています。

「オブジェクト指向ソフトウェアの大規模開発には、フレームワークが要求されます。設計が2つのオブジェクトの相互作用を要求するたびに、開発者が相互作用が機能する方法について新しい概念を提案する必要がないフレームワークを決めることが重要です。」(『J2EE Blueprints』の「セクション10.2.2.2」)

フレームワークをベースとしたアプローチを使用すると、開発チームにおける開発者の数が増えるにつれて、生産性があがり、拡張性が強化されます。それぞれの開発者に、すべてのパターンを手動でコーディングする方法を教えるよりも、フレームワーク・オブジェクトを適用してソリューションを実現する方法を教える方が効果的です。通常は、既製のフレームワークを購入するか、独自にフレームワークを作成するかを開発チームで決定する必要があります。

既製のものを使用しない開発チームでも、コーディング・パターンをヘルパー・クラスにして汎用的にできることがわかるようになると、段階的なアプローチによって最後には必ず開発チーム独自のフレームワークを作成するようになります。ただし、このように、開発チーム独自のフレームワークの作成に至る手法には、マイナス面があります。それは、フレームワーク全体のデザインを最初から設計する場合には、J2EEデザイン・パターンで要求しているような「結合性」のあるコラボレーションセットを実現できますが、段階的にフレームワークへと到達する方法では、その「結合性」が弱くなってしまうことです。また、一般的には、フレームワークを独自に作成することは、自分たちでない開発者のために、フレームワーク開発チームのやる気を持続させ、かつ専念させなければならないことを意味します。しかも、自分たちはそのフレームワークによるメリットを得られないのです。現実には、「自分たちでフレームワークを開発する」という方針に基づいて作業を始めた開発チームの多くは、最高のスタッフおよび最高の状態で臨んだとしても、結果的に莫大な労力と時間がかかることになってしまいます。

J2EEに対するアプローチの比較(手動コーディングとフレームワークの使用)

J2EEのデザイン・パターン・フレームワークを設計、編集、デバッグ、ドキュメント化、配布し保守をおこなうことは、莫大な時間と労力がかかります。そのため、これら負荷の高い作業を不要にするためにJ2EEパターンを実装済みのBusiness Components for Java(BC4J)を始めとした既製のJ2EEフレームワークを利用する開発チームがますます増えています。このアプローチは、J2EEアプリケーションを短期間で構築、デプロイ、および拡張するうえで役に立ちます。

ただし、フレームワーク・ベースのアプローチを使用してJ2EEデザイン・パターンを実装したことがない場合は、フレームワークの開発でどのような労力が軽減されるのか、すぐにはわからない可能性があります。デザイン・パターンに沿ったコーディングで必要な労力について理解するために、Java Pet Store Demoの例を分析した上で、BC4JなどのJ2EEフレームワークを使用して、同じ例がどのように実装されるのか見てみましょう。

Pet Store Demoの簡単なマスター/ディテールJSPページ

Pet Store Demoでは、複数のJSPページによって店のカタログを参照するためのユーザー・インターフェースが提供されています。product.jspページは、このようなインターフェースを提供するJSPページの代表的なサンプルです。このページはマスター/ディテールのビジネス情報を表示するものであり、同じ手法が多くのWebアプリケーションのページに活用できます。このホワイトペーパーでは、デモの1.1.2バージョンに対応したPet Storeのデモについて説明しています。

このJSPページは最上部に標準のPet Storeバナーを表示した後、「Chihuahua」といった商品(Product)の種類の名前が表示され、その種類に対応して店内の商品(Product)の詳細項目(Item)が表示されます。ページ内には動的な内容が図1のように表示されます。

Pet Store DemoのJSPページの動的なマスター/ディテール内容
図1: Pet Store Demoの動的なJSPページのマスター/ディテール

これらの結果を生成するproduct.jsp例2に示します。ここで使用されるタグ・ライブラリはJ2EEプラットフォームの組み込みパートではないことを明確にするために、デモ・ページで使用されている接頭辞j2eeを、わかりやすいようにpetdemoという接頭辞に変更しています。

<%-- Product.jsp:Lists all items in the inventory for a particular product type.  --%>
<%-- Note!Renamed "j2ee" tag library prefix to "petdemo" for clarity --%>
<%@ taglib uri="/WEB-INF/tlds/taglib.tld" prefix="petdemo" %>
<%@ page import="com.sun.j2ee.blueprints.shoppingcart.catalog.model.Product" %>
<jsp:useBean id="catalog" scope="application"
             class="com.sun.j2ee.blueprints.petstore.control.web.CatalogWebImpl"/>
<%
  Product product = catalog.getProduct(request.getParameter("product_id"));
%>
<petdemo:productItemList numItems="3" productId='<%=request.getParameter("product_id")%>'>
  <font size="5" color="green"><%= product.getName() %></font>
  <table border="0" bgcolor="#336666">
    <tr background="../images/bkg-topbar.gif">
      <th><font color="white" size="3">Item ID</font></th>
      <th><font color="white" size="3">Item Name</font></th>
      <th><font color="white" size="3">Item Price</font></th>
    </tr>
    <petdemo:items>
      <tr bgcolor="#eeebcc">
        <td><petdemo:productItemAttribute attribute="id"/></td>
        <td>
          <petdemo:productItemAttribute attribute="productAttribute"/>
          <petdemo:productItemAttribute attribute="name"/>
        </td>
        <td><petdemo:productItemAttribute attribute="listcost"/></td>
      </tr>
    </petdemo:items>
  </table>
</petdemo:productItemList>
例2: Pet Store DemoのProduct.jspページの簡易バージョンのソース・コード

<jsp:useBean>タグから、product.jspページでCatalogWebImplというBeanを使用していることがわかります。このクラスは、"Model/View/Controller"パターンにおけるアプリケーション・オブジェクトを実装していて、アプリケーション・タスクに応じてWeb層でページをレンダリングするために必要となるデータ・コレクションへのアクセスを集中できます。この例では、店のカタログから商品(Product)、項目(Item)、およびカテゴリ(Category)を参照するというタスクになります。このクラスは、以下のメソッドによってタスクに応じたデータ・モデルを取得します。

これらのメソッドを使用すると、Web層の開発者は、状況に応じて単一のデータ項目、またはデータ・コレクションのいずれかにアクセスすることができます。product.jspページはgetProduct()をコールして、product_idというHTTPパラメータに渡されるIDに対応する、Productという値オブジェクトのインスタンスを取り出します。ページに商品名を表示するために、このページの後半で使用されているJavaスクリプトレット<%=product.getName()%>にあるように、getName()メソッドをコールしています。

選択した商品(Product)に対応する商品項目(Item)の一覧を表示するために、以下の内容が含まれているカスタム・タグ・ライブラリを使用します。

Productの情報とItemの詳細情報との間のマスター/ディテール関係を調整するために、次のJSPスクリプトレットを使用して、マスターのproduct_idパラメータの値を、<petdemo:productItemList>タグのproductId属性の値として渡しています。

<petdemo:productItemList numItems="3" productId='<%=request.getParameter("product_id")%>'>

これらの各タグはPet Store Demoの商品項目一覧に特有のものですから、実際に同様のアプリケーションを作成する場合には、デモ・ソース・コードのProductItemListTag.javaItemsTag.java、およびProductItemAttributeTag.javaファイルのそれぞれに類似したコードが必要になることに注意してください。これらのタグによって、データのバインドおよびフォーマットのためのコードは、JSPページの開発者から見えなくなります。たとえば、ProductItemAttributeTag.javaクラスには、例3で示されるようなコードが含まれています(コメントを追加しています)。

// <petdemo:productItemAttribute attribute="XXXX"/> タグの処理
if (attribute.equalsIgnoreCase("name")) {
  return (product.getName());
} else if (attribute.equalsIgnoreCase("productattribute")) {
  return (productItem.getAttribute());
  // ...etc...
} else if (attribute.equalsIgnoreCase("listcost")) {
  // 通貨形式で値を書式化
  return JSPUtil.formatCurrency(productItem.getListCost(), locale);
} else if (attribute.equalsIgnoreCase("description")) {
  return (product.getDescription());
  // ...etc...
例3:  <productItemAttribute>タグのデータ・バインド/フォーマット・コードの実装

さらに、これらのタグ実装クラスでは、CatalogWebImplアプリケーション・オブジェクトも使用します。これは、ページで詳細情報を表示するのに必要なItem値オブジェクトのコレクションにアクセスするためです。

"Data Access Objects"パターンで推奨しているように、メンテナンスが簡単になるようデータアクセス・コードは別のクラスに定義します。そのため、CatalogWebImplクラスは、その中でデータを取り出すために、JDBCコードによって実際にSQL問合せを実行するCatalogDAOImplクラスを使用します。
ここで、"Fast-Lane Reader"パターンが実装されています。つまり、店のカタログ・データにアクセスする際に、意識的にWeb層のアプリケーション・オブジェクトでEJBエンティティBeanを使用しないようにし、代わりに直接JDBCでアクセスします。

具体的には、CatalogDAOImplクラスのコードにはproduct.jspページで必要なデータを取得するために実行される、2つのSQL文を埋め込みます。CatalogDAOImplgetProduct()メソッドでは、次の行によって、IDによる商品(Product)の検索をおこないます。

String qstr = "select productid, name, descn " +
        "from " +
        DatabaseNames.getTableName(DatabaseNames.PRODUCT_TABLE, locale) +
        " where " +
        "productid='" + productId + "'";

また、IDによって商品(Product)を検索するために、getItems()メソッドには次の行が含まれています。

String qstr = "select itemid, listprice, unitcost, " +
        "attr1, attr2, a.productid, name, descn " +
        "from " +
        DatabaseNames.getTableName(DatabaseNames.ITEM_TABLE, locale) +
        " a, " +
        DatabaseNames.getTableName(DatabaseNames.PRODUCT_TABLE, locale) +
        " b where " + "a.productid = '" + productId +
        "' and a.productid = b.productid "+ " order by name";

ここで、詳細情報を表示するために必要なPRODUCT表とITEM表の情報を結合します。さらに、CatalogDAOImpl.javaに追加すべき重要なコードとしては以下の実装が含まれます。

  1. データベース接続の取得
  2. JDBCステートメントの作成
  3. 問合せの実行
  4. 前のページで取得済みの行をスキップする("Value List Handler(Page-by-Page Iterator)"の実装の一部)
  5. 現行ページに、結果セットの行に対するItem値オブジェクトを作成する
  6. JDBCリソースのクリーンアップ
  7. これらのItemオブジェクトのリストを呼出し元に返す

Pet Storeのデモには、これらのクラスを各自で開いて参照できるように、すべてのソース・コードが添付してあります。すべてのコードには、アプリケーションで実行されるひとつひとつの問合せを記述する必要があります。図4では、J2EEデザイン・パターンに対して手動でコーディングするというしたアプローチを使用した場合に、簡単なJSPページにマスター/ディテール情報を表示するのに必要なアプリケーション・プログラミング・コード(のクラス)を表しています。

J2EEデザイン・パターンに従ってマスター/ディテール・ページを実装するのに必要なコード
図 4: J2EEデザイン・パターンに従ってマスター/ディテール・ページを実装するのに必要なコード(クラス)

具体的には、デザイン・パターンを実装するためのPet Store Demoのアプローチにより、次の内容を実装しなければならないことがわかります。

  1. タスクに応じたMVCアプリケーション・オブジェクトCatalogWebImpl):

    タスクに対するクライアント表示の実装に必要なデータ・モデルを明確に区別します。

  2. "Data Access Objects"実装クラス(CatalogDAOImpl):

    JDBCデータベース・アクセス・コードを分離して"Fast-Lane Reader"パターンを実装し、かつ、前ページで取得済みの行をスキップして現行ページに必要なサイズの行リストだけを返すという"Value List Handler(Page-by-Page Iterator)"の動作を実装します。

  3. 2つの"Transfer Object(Value Object)"クラス(ProductItem):

    商品(Product)および項目(Item)の問合せに対する各データベース問合せの結果行を転送します。

  4. カスタムJSPタグ(ProductItemListTagProductItemAttributeTag):

    データ・バインドおよびフォーマットのコードなどのコーディングの詳細をJSPページの作者から隠蔽するレイヤを実装します。

1つの商品、およびその詳細項目の一覧を画面で取得するには、これらのすべてのステップ(およびバックグラウンドで要求されるコード)が必要でした。

おそらく、顧客情報などの他のデータをJSPページで表示する際に、これらのクラスの中で再利用できるものがないということは非常に残念なことです。Webクライアントでの表示が求められる、顧客情報などのビジネス情報の個別のビューについて、上記のすべてのステップを複製しなければなりませんが、これは非常に煩雑な作業です。

開発者が、似たようなソリューションを何度も手動でコーディングする場合には、何を省略できるかについてよく考える必要があります。実際に経験者であれば、コードの品質で最初に問題となるものとして、「JDBCを最適に使用するための特別なコーディング」の存在を挙げています。アプリケーションのデータベース・アクセス層での不用意なコードは、アプリケーションのパフォーマンスおよび拡張性に、直接マイナス影響を与えます。たとえば、Pet Store DemoのCatalogDAOImpl.javaのJDBCコードは、JDBCバインド変数を使用しない、プリペアド・ステートメントを再利用しない、大量のレコードのフェッチを利用しない、頻繁に取り出されるデータに対してデータ・キャッシングを実装しない、などの特徴があります。理想的には、こういった詳細なアプリケーション・テクニックは、いったん標準的なコードとして記述して、開発チーム内のすべての開発者がメリットを共有できるようにすべきです。

フレームワーク開発におけるアプローチ

前述のSunの『J2EE Blueprints』で見たように、このように繰り返し発生するルーチン的な問題を解決するには、関連するさまざまなオブジェクトの相互作用を自動化するフレームワークが有用です。実際に、前述の手動コーディングのパターン例に従って、JSPを10ページも実装したら、ほぼ間違いなく、共有できる機能はないのか考えると思います。

経験的に、(店のカタログの表示などの)アプリケーション・タスクに関連して作成したすべてのページに対して、以下のような同じ質問に答えるためのコードを入力していることに気が付きます。

また、手動でコーディングした値オブジェクトは、それぞれ類似のコーディング・パターンに従っており、もっと汎用的に簡単に作成できることにも気づくでしょう。繰り返しおこなわれるデザイン・パターンのコードへの実装を汎用化して表すと図5のようになります。

汎用的なデザイン・パターン実装は、ルーチン的な問題に対してメタデータを使用することで解決策となる
図5: 汎用的なデザイン・パターン実装は、ルーチン的な問題に対してメタデータを使用することで解決策となる

設計、デバッグおよび実装のために十分な時間を費やすことが許されるのであれば、次のような汎用的なJava Bean コンポーネントを自身で記述して、J2EEデザイン・パターンの開発を単純にできることがわかります。

優れたオブジェクト指向のデザインの実現するために、これらの汎用的なヘルパー・コンポーネントの動作はQueryBeanImplApplicationObjectImplなどのベース・クラスに定義すべきでしょう。それ以降、作成中の個別のJ2EEアプリケーションにおいて新しい問合せ、および新しいアプリケーション・オブジェクトを作成する必要がある場合には、適切な汎用ベース・クラスから新しいサブクラスを作成します。SQL問合せ文、問合せ結果の列の名前と型、またはアプリケーション・オブジェクト・データ・モデルのビューの名前など、特徴のあるメタデータは、編集のしやすさも考慮して、それぞれのコンポーネントに対応したXMLファイルに格納します。J2EEアプリケーションの実装方法をこのように共通化することで、汎用的なJSPタグ・ライブラリを作成できるようになります。これによって、表示が必要なデータに対するデータ・バインドがすべて処理されるため、データ・タイプが新しくなる度に個別にコードを用意する作業が不要になります。

時間の節約につながるフレームワークを作成したからといって、アプリケーションの「J2EE準拠性」が弱くなることはありません。新しい、より高度なアプローチは、これまで通りJ2EEデザイン・パターンを実装しています(そのように設計されているのですから)。変わったことといえば、デザイン・パターンのプログラミング・コードを実装するための時間が減少したこと、そして、企業の優位性を確立するためのJ2EEアプリケーションを実装する時間が増えたことです。この理想的なシナリオで、新しいJ2EEフレームワークは、さまざまなメリットをもたらしてくれる素晴らしい「贈り物」だということがわかって頂けることでしょう。

実情では、このようなフレームワークを独自に実装するための時間があらかじめスケジュールされているということは、ほとんど考えられません。しかし、時間が十分でなくても、デザイン・パターンを手動でコーディングするしかない、というわけではありません。このような場合には、オラクルが提供するJ2EEデータアクセス・フレームワーク(Business Components for Java(BC4J))を利用することができます。これは、前述のJ2EEデザイン・パターン・フレームワークをすでに実装したコンポーネントで、開発者はデザイン・パターンを実装する必要がありません。

BC4Jを使用したPet Store Demoの実装

BC4Jフレームワークには、前述のように開発期間を短縮する要素として、次の実装が用意されています。

BC4Jのウィザードとエディタを使用すると、汎用的なフレームワーク・コンポーネントのサブクラスを容易に作成することができます。このツールは、Javaフレームワーク・サブクラスとなるJavaクラス、およびXMLベースのメタデータ管理ファイルの作成や編集を管理します。ここで記述するのは、各自のアプリケーションの機能に特有のコードのみです。

Oracle9i JDeveloper開発環境の空のプロジェクトから開始した場合には、前述のPet Store DemoからJSPページを実装するために必要となるのは次のステップです。

  1. コンポーネントを保持しておくためのJavaパッケージの名前を指定します。ここでは、petという名前にします。
  2. ビュー・オブジェクト ウィザードで次のSQL問合せを使用して、Productというビュー・オブジェクトを作成します。

    select productid, name, descn
     from product
     where productid = ?
  3. もう一度ビュー・オブジェクト ウィザードで次のSQL問合せを使用して、ProductItemというビュー・オブジェクトを作成します。

    select itemid, listprice as listcost, unitcost,
    attr1 as product_attribute, a.productid, name, descn
     from item a, product b
     where a.productid = b.productid
     order by name
  4. ビュー属性エディタを使用して、Listcost属性の書式プロパティで「Currency」として「$0000.00」の形式を設定します。
  5. ビュー・リンク ウィザードを使用してItemsForProductというビュー・リンクを作成します。これは、宣言的に指定されたキー属性に基づいて、マスターのProductビューとディテールのProductItemビューのリンクを作成します。
  6. アプリケーション・モジュール ウィザードを使用して、CatalogWebというアプリケーション・モジュールをMVCアプリケーション・オブジェクトとして作成します。また、ProductビューとProductItemビューの組合せでマスター/ディテールを選択し、アプリケーション・オブジェクトがサポートするデータ・モデルを定義します。
  7. JDeveloperのJSPエディタで提供されるタグ・ライブラリ記述支援機能を使用して、product.jspページを、次のように記述します。
  8. 最後に、ページの先頭に1行のJSPコードを追加して、Product問合せのJDBCバインド変数の値が、product_idというHTTPパラメータの値と等しくなるようにします。

    product.getRowSet().setWhereClauseParam(0,request.getParameter("product_id"));

次に、開発環境から直接product.jspページを実行して、結果を確認します。わずかなコードを記述するだけで、Pet Store Demoの例と同じ結果が生成されます。実際に記述する必要があるのは、バインド変数に設定するための上記の1行のみです。図6は、作成したコンポーネントのセットについて示しています。

BC4Jを使用したアプローチ: フレームワーク・クラスを拡張するコンポーネント(メタデータ・ドリブン)
図6: BC4Jを使用したアプローチ: フレームワーク・クラスを拡張するコンポーネント(メタデータ・ドリブン)

JSPページの全体は例7のようになります。

<%-- Lists all items in the inventory for a particular product type.--%>
<%@ taglib uri="/webapp/DataTags.tld" prefix="bc4j" %>
<bc4j:ApplicationModule id="catalog" configname="pet.CatalogWeb.TestConfig"/>
<bc4j:DataSource id="product" appid="catalog" viewobject="Product"/>
<bc4j:DataSource id="item" appid="catalog" viewobject="ProductItem" rangesize="3"/>
<%
 product.getRowSet().setWhereClauseParam(0,request.getParameter("product_id"));
%>
<bc4j:RowsetIterate datasource="product">
 <font size="5" color="green"><bc4j:ShowValue dataitem="Name"/></font>
 <table border="0" bgcolor="#336666">
  <tr background="../images/bkg-topbar.gif">
   <th><font color="white" size="3">Item ID</font></th>
   <th><font color="white" size="3">Item Name</font></th>
   <th><font color="white" size="3">Item Price</font></th>
  </tr>
  <bc4j:RowsetIterate datasource="item">
   <tr bgcolor="#eeebcc">
    <td><bc4j:ShowValue dataitem="id"/></td>
    <td>
     <bc4j:ShowValue dataitem="ProductAttribute"/>
     <bc4j:ShowValue dataitem="Name"/>
    </td>
    <td><bc4j:RenderValue dataitem="Listcost"/></td>
   </tr>
  </bc4j:RowsetIterate>
 </bc4j:RowsetIterate>
</table>
<bc4j:ReleasePageResources/>
例7: BC4Jを使用したアプローチ: 汎用的なタグ・ライブラリと1行のみのコード

Pet Store Demoでは、このカタログ・ページのセットに対して敢えてエンタープライズBeanを使用しないで、代わりにJSPページに対してMVCアプリケーション・オブジェクトを直接使用し、DAO実装クラスをローカルJava Beanクラスとして間接的に使用しています。ただし、Pet Store Demoの他のページでは、セッションBeanを使用しています。BC4Jを利用した強力なJ2EEアプリケーション開発では、どちらの方法も使用できます。どのアプリケーション・コンポーネントをデプロイすべきか、どれを前述のローカルJava Beansとして使用するか、どれをEJBセッションBeanとしてデプロイし、さらには"Session Facade"パターンをサポートさせるか、といったことをいつでも柔軟に選択できます。これはアプリケーション・モジュール・エディタを使用して、図8に示されている「リモート」タブで対象のデプロイ・モードを選択するだけで済みます。

任意のアプリケーション・モジュールをEJBセッションBeanとして簡単にデプロイする
図8: 任意のアプリケーション・モジュールをEJBセッションBeanとして簡単にデプロイする

次のセクションでは、BC4JがEJBアーキテクチャをどのようにサポートするのか、および高度なEJBアプリケーションを、(Oracle9iAS、WebLogicなどの)さまざまなJ2EEアプリケーション・サーバー用に実装する際に、BC4J開発者がどのオプションを使用できるかを詳しく説明します。

BC4Jを使用したEJB開発の効率化

J2EE Blueprints』のデザイン・パターンに記載されている内容、およびJava Pet Store DemoでさまざまなJ2EEテクノロジが混在しているところからわかるように、Enterprise JavaBeansは、J2EE開発者が使用するたくさんのツールのなかのひとつに過ぎません。BC4Jはこの概念に従って、さまざまなJ2EEテクノロジを厳選して、EJB 1.1とEJB 2.0アプリケーション・サーバーを対象としたJ2EEアプリケーション構築機能の完全なセットを実装しています。BC4Jフレームワークのアーキテクチャ、機能、およびパフォーマンスは、JSP、サーブレット、Swing、およびXMLユーザー・インターフェースを使用しているオラクルのアプリケーション開発部門のプロジェクトで、2年間にわたり日常的に使用され、さまざまなアプリケーションで検証されています。

最初に、EJBアプリケーションを構築するための多様なアーキテクチャの中で、BC4Jがターゲットとしたアーキテクチャを選んだ理由について学習します。

多くのEJBアーキテクチャの中で突出したパフォーマンス

EJBのトレーニング・コースが終了すると、初めてJ2EEによる開発をおこなう人の多くは、Enterprise JavaBeansがJ2EEツールボックスの主要なツールであることを自然に理解しているでしょう。この最初に感じた印象に従って、J2EE開発者の多くは、アプリケーションのすべてのコンポーネントに対して以下のエンタープライズBeansを作成しようとします。

ただし、通常は開発者がアプリケーションをロードテストした際に、エンタープライズBeanで必要なリモート処理固有のオーバーヘッドによって、システムの拡張性において問題があることに気づきます。『J2EE Blueprints』の「セクション5.5」で、このことを認めており、単純で実用的な方針を推奨しています。

「エンタープライズBeanは、大量のシステムリソースとネットワーク帯域幅を消費するリモートオブジェクトであるため、すべてのビジネスオブジェクトをエンタープライズBeanとしてモデル化することは適切ではありません。クライアントから直接アクセスされる必要があるビジネスオブジェクトだけをエンタープライズBeanにする必要があります。他のオブジェクトは、データベースアクセスをカプセル化するデータアクセスオブジェクト、およびエンタープライズBeanに依存する、きめ細かいオブジェクトをモデル化する 値オブジェクトとしてモデル化できます。」(『J2EE Blueprints』の「セクション5.5」)

"Session Facade"パターンと呼ばれるJ2EEデザイン・パターンは、セッションBeanのみをリモートでアクセスできるようにアプリケーションを設計することを強く推奨しています。"Session Facade"パターンでは、クライアントに対して個別のビジネス・エンティティへのきめ細かいリモート・アクセスを提供するよりも、EJBセッションBeanを作成してビジネス情報の適切なビューに対するアクセスを、タスク特有のサービス・インターフェースでラッピングするよう提案しています。これは図9に示すように、値オブジェクトのクラスによって、ビジネス・データのサブセットのコピーが、セッションBeanとクライアント・プログラムの間でやり取りされることを意味します。

セッションBean Facadeで永続的なビジネス・エンティティへのアクセスをカプセル化する
図 9: セッションBean Facadeで永続的なビジネス・エンティティへのアクセスをカプセル化する

推奨される"Session Facade"アーキテクチャでは、永続的なビジネス・エンティティを表す際に使用するテクノロジを効率よくカプセル化しています。J2EEプラットフォーム(具体的にはEJB仕様)では、このようなビジネス・エンティティを実装するための方法をいくつかのオプションから選択することができます。セッションBean Facadeでは、最上位レベルに2つの方針があります。永続ビジネス・コンポーネントは、次のように実装することができます。

  1. EJBエンティティBeanとして
  2. 軽量データアクセス・オブジェクト(DAO)として(前述の『J2EE Blueprints』の上記の引用で提案されています)

EJBを紹介しているテキストブックの多くがソリューションとして提案しているために、エンティティBeanは明らかに認知度といった理由で選択される傾向にあります。ただし、『J2EE Blueprints』などの、EJBの詳細なガイドブックでは、従来のリモート処理のオーバーヘッドを考えると、実用的な選択肢として軽量のデータアクセス・オブジェクトを勧めています。さまざまなテクノロジの選択肢がある中で、エンティティBeanはその中心となっていることは事実ですが、一方で、かつてエンティティBeanを使用して教訓を得たJ2EEの開発者の間では、軽量DAOソリューションの方が支持されています。

エンティティBeanには、コンテナ管理による永続性(CMP)とBean管理による永続性(BMP)の2つのオプションが用意されており、開発者は、生産性と柔軟性のどちらを優先するかを選択することになります。一方、軽量DAOのアプローチでは、BMPアプローチしか使用できません。CMPエンティティBeanは、永続コードを手動で記述しないことによって時間を節約できるという、明白な選択肢のように見えますが、BMPエンティティBeanと軽量DAOの場合でも、オブジェクト/リレーショナル・マッピング・フレームワークを使用することによって、手動のコーディングを減少させることが可能です。このため、フレームワークを活用できるとすれば、軽量DAOのアプローチは決して劣っていません。

CMPエンティティBeanでは、永続性マッピングをXMLベースのデプロイメント・ディスクリプタに指定するため、保守が簡単になります。ただし、BMPエンティティBeanまたは軽量DAOとともに使用しようとしている永続性フレームワークで類似のメタデータ・ドリブン・アプローチが採用されている場合は、両者のアプローチは非常に類似したものとなります。

CMPエンティティBeanでは、アプリケーション・サーバーにおける移植性が保証されますが、適切に記述されたBean管理の永続性を採用したフレームワークにおいても移植性は提供されます。さらに任意のJ2EEアプリケーション・サーバーで簡単に実行することが可能で、複数のバックエンド・データベースをサポートするように設計することも可能です。このように考えると、アプローチの選択はまだ互角と言えます。

幸いなことに、最後にアプローチの選択をするうえで参考となる2つのヒントがあります。

  1. EJB 1.1の仕様に準拠しているCMPおよびBMPの両方のエンティティBeanはリモート処理が可能なオブジェクトでなければなりません。このようなオブジェクトは、余計なオーバーヘッドがかかります。
  2. 軽量DAOは、継承のようなJavaの機能や高度なSQLなどを自由に利用できます。これらの機能は、標準のエンティティBeanを使用しても、簡単にはサポートできません。

このため、図10の表に示すように、高度な生産性と柔軟性を提供し、オーバーヘッドを少なくするような理想的なテクノロジの組合せは、軽量データアクセス・オブジェクト・フレームワークで、メタデータ・ドリブンのBean管理による永続性を使用したものだといえます。

EJBの最適な持続性アーキテクチャ: メタデータ・ドリブン、フレームワークベースのDAO
図10: EJBの最適な永続性アーキテクチャ: メタデータ・ドリブン、フレームワークベースのDAO

この組合せでは、メタデータの記述に基づいてフレームワークが永続性を実装するため、CMPエンティティBeanの利便性が実現され、保守性が改良されます。また、手動コーディングに伴う煩雑さに悩まされることなく、BMPソリューションのみで可能な柔軟性とコントロールが提供されます。この組合せは、EJB 1.1とEJB 2.0の両方でサポートされている技術であるため、広範囲にわたるアプリケーション・サーバーで移植性が実現されます。最後に特筆すべきこととして、EJB 1.1のエンティティBeanを利用する際のリモート処理のオーバーヘッドが発生しないようになります。これは、"Session Facade"パターンではエンティティBeanのリモート処理が不要になるためです。ビジネス・エンティティは、Facadeによって全てカプセル化されるため、リモート処理が不要になります。

要約すると、メタデータ・ドリブンのオブジェクト/リレーショナル永続性フレームワークを使用してビジネス・エンティティを軽量DAOとして実装すると、メリットのみが得られます。"Session Facade"パターンを実装するEJBセッションBeanを組合せるという手法は、『J2EE Blueprints』で支持されているテクノロジであり、有効なJ2EEアプリケーションとして普及しつつあります。

「J2EEアプリケーションアーキテクチャのデータアクセス・オブジェクトを使用することで、アプリケーション開発者とサービスプロバイダの担当領域の空白部分がなくなります。これにより、ツールベンダーはその価値を高めることができます。将来的には、サンプルアプリケーションで使用しているようなカスタムデータアクセスオブジェクトは、高度なオブジェクトリレーショナルツールに置き換えられると思われます。」(『J2EE Blueprints』の「セクション5.5.1.4」)

次のセクションでは最少の労力で最適なアプローチを得ようとしているJ2EE開発者に対してBC4Jフレームワークが提供する、柔軟で、かつJ2EEに完全準拠したテクノロジの組合せについて説明します。

BC4J データアクセス・オブジェクト機能の概要

BC4Jは、本質的にはメタデータ・ドリブンのデータアクセス・オブジェクト・フレームワークです。フレームワークを使用すると、J2EE開発者は時間を節約することができます。この機能によって、J2EEアプリケーションのすべてのアーキテクチャ層(以下参照)でメリットを得ることができます。

このセクションでは、フレームワークを構成する主要な構築ブロック・コンポーネントについて詳しく見てみましょう。図11は、BC4Jを使用した典型的なJ2EEアプリケーション構築のアーキテクチャを示しています。

BC4Jを使用した典型的なJ2EEアプリケーション構築のアーキテクチャ
図11: BC4Jを使用した典型的なJ2EEアプリケーション構築のアーキテクチャ

この図は、簡単な支払管理アプリケーションを表しています。これを構成するBC4Jフレームワークの中核となるコンポーネントは以下のとおりです。

Oracle9i JDeveloperには、J2EEアプリケーションでBC4Jフレームワーク・コンポーネントを使用できるようにするための総合的なツールが用意されています。ユーザーは対話型のウィザード、エディタ、プロパティ・インスペクタ、およびUMLダイアグラマを使用して、BC4Jコンポーネントを作成、拡張、デプロイできます。もちろん、これらのツールは、高速なデバッグ、デプロイ、およびソースコード管理のサポートが可能な開発環境内で密接に統合されているため、End-to-Endで、J2EEアプリケーション開発のライフサイクルを処理することができます。

たとえば、図11のアプリケーションに対するセッションBean Facadeを構築するためには、空のUMLクラス・モデルを作成するところからスタートできます。ダイアグラム内に新しい3つのエンティティ・オブジェクトを作成し、それらの対象のクラス名を入力して、属性を直接ダイアグラムに追加します。次に、ダイアグラムのクラスを関連付けして、多重度を設定します。結果は、図12のようになります。

関連するエンティティ・オブジェクトを表すOracle9i JDeveloper UMLクラス・モデル
図12: 関連するエンティティ・オブジェクトを表すOracle9i JDeveloper UMLクラス・モデル

JDeveloperは、ユーザーの作業中に、ダイアグラムでおこなったアクションと同期してリアルタイムに、それぞれのデータアクセス・オブジェクトに対するJavaソースとXMLデプロイメント・ディスクリプタを更新します。たとえば、図13は、前述のUMLモデルを作成した後で、システム・ナビゲータと構造ペインがどのように見えるかを表しています。

フレームワーク・コンポーネントを表すシステム・ナビゲータと構造ペイン
図13: フレームワーク・コンポーネントを表すシステム・ナビゲータと構造ペイン

エンティティ・オブジェクト・エディタを使用すると、BillCustomer、およびPaymentエンティティのオブジェクト/リレーショナル・マッピングを指定できます。その後、データベース・オブジェクト作成のメニューを選択して、データベースにBillCustomer、およびPaymentインスタンスを格納するためのリレーショナル表を自動的に作成できます。

この支払管理アプリケーションでは、支払の遅い顧客をグループとして操作したり、これらの個別の顧客や対応する延べ払いについて作業したりすることが要求されています。これをおこなうために、ビュー・オブジェクト・ウィザードを使用して、SlowPayingCustomersおよびLatePaymentsという名前のコンポーネントを作成し、必要な情報のみを取得するSQL問合せを記述することができます。図14は、ビュー・オブジェクト・ウィザードの1つのパネルを示しています。開発者は、これを使用して完全な問合せを指定することができます。このような高度な柔軟性が必要ない場合は、ウィザードの他のパネルでエンティティ・オブジェクト属性を選択すれば、その情報に基づいてデフォルトの問合せ文を自動的が定義されます。

ビュー・オブジェクトの「エキスパート・モード」で柔軟性の高い複雑なSQL問合せを使用する
図14: ビュー・オブジェクトの「エキスパート・モード」で柔軟性の高い複雑なSQL問合せを使用する

この2つの問合せ(ビュー・オブジェクト)を結合させるビュー・リンク・コンポーネントを導入すると、アプリケーション・コードで、必要な相互関連情報のマスター/ディテールのセットをより簡単に操作することができます。最後にPaymentTermsManagementAppというアプリケーション・モジュール・コンポーネントを作成し、リンクされているビュー・コンポーネントを、テストとデプロイをおこなうために1つのMVCアプリケーション・オブジェクトにグループ化することができます。図15に示されているような、便利な組み込みのテスト・ツール(Component Browser)を使用すると、アプリケーション・モジュールをJ2EEサーバーにデプロイする前に、ローカル環境でテストすることもできます。

組み込みのComponent Browserを使用したアプリケーション・モジュールのテスト
図15: 組み込みのComponent Browserを使用したアプリケーション・モジュールのテスト

フレームワーク・コンポーネントによって提供されるデータアクセス機能を十分使用できるようになると、データアクセス・オブジェクトに対して"Session Facade"となるような完全なEJBセッションBeanとしてデプロイするためには、たった1つのウィザードを実行するだけで良くなります。システム・ナビゲータのプロジェクトから「ビジネス・コンポーネントの配布...」オプションを選択すると、図16のようなビジネス・コンポーネントの配布ウィザードが表示されます。このウィザードを使用して、対象とするEJBセッションBeanの種類、トランザクションのタイプ(Bean管理またはコンテナ管理)、ターゲットのEJBサーバーを選択することができます。

対象のEJBセッションBeanタイプおよびターゲットのEJBサーバーを選択してデプロイする
図16: 対象のEJBセッションBeanタイプおよびターゲットのEJBサーバーを選択してデプロイする

いったんこれらの選択をおこなうと、ウィザードで、J2EE準拠のEARファイルがデプロイのために自動的にアセンブルされます。また、Oracle9iASやWebLogicへデプロイする場合は、このツールによってデプロイの最終ステップまで誘導されるため、EARファイルのサーバーへのデプロイが実際に直接実行されてユーザーの介入は不要になります。この後のセクションの中で、図16の「AppModuleセッションBean」と「サービス・セッションBean」の違いについて説明しますので、選択すべき機能の違いが明確になることでしょう。

まず問合せBeanであるビュー・オブジェクトとビジネスBeanであるエンティティ・オブジェクトが協調動作するという画期的な方法で提供される機能について学習します。このペアのオブジェクトは、BC4Jデータアクセス・オブジェクトの実装の陰と陽(光と影)のようなものです。

BC4J データアクセス・オブジェクトの理解

BC4Jのビュー・オブジェクトとエンティティ・オブジェクトを組み合わせると、開発者が"Fast-Lane Reader"パターンのSQL問合せとエンティティBeanを組み合わせて更新を処理した場合と同じ機能が実現されます。ただし、このセクションで説明するように、BC4Jデータアクセス・オブジェクトを使用すると、他に多数の機能が実現され、より高度なパフォーマンスが実現できるうえエンティティBeanのアプローチを使用した場合のわずかなデメリットも回避されます。

"Fast-Lane Reader"パターンのSQLとエンティティBeanの組合せによるデメリット

"Fast-Lane Reader"というJ2EEデザイン・パターンでは、アプリケーションでデータを取得するための最も高速な方法は、JDBCを使用してSQL文の結果を読み込むことであるとしています。

リストのデータが最新になっていることよりも、データリストに効率よくアクセスする方が重要な場合もあります。"Fast-Lane Reader"パターンは、リソースから大量のアイテムのリストをスムーズに取り出せるようにします。アプリケーションは、エンティティBeanを使用しないで、データアクセス・オブジェクトによって、ダイレクトにデータアクセスします。(Sunの『デザイン・パターン・カタログ』の"Fast-Lane Reader"のページ)

この直接的なJDBCアプローチは、典型的なEJBのチュートリアルで扱うものとは少し違います。代わりにEJBのテキストでは、findByPrimaryKey()、およびユーザー定義のfinderメソッドを正式な方法として使用してデータを取り出します。その一方で"Fast-Lane Reader"デザイン・パターンは次の理由で生まれました。

リレーショナル・データベースは、ビジネス情報の高速なフィルタリング、落とし込み、および結合をおこなうための強力なエンジンへと進化を続けています。"Fast-Lane Reader"パターンでは基本的に、「最も早く結果を得るには、SQL 文を使用してどのデータが必要なのかをデータベースに通知し、フェッチすることである」としています。たとえば、図17に示されているように、"Fast-Lane Reader"のSQL文を使用してデータをフェッチし、 EJB Session Facadeからクライアントへ返すために値オブジェクトのコレクションにデータを挿入するといった具合です。

EJBセッションBeanで"Fast-Lane Reader"パターンを使用する
図17: EJBセッションBeanで"Fast-Lane Reader"パターンを使用する

また図17は、クライアントが問合せしたデータに対して変更を加えて返す場合に、エンティティBeanを直接使用して変更をおこなうコードを記述し、変更を永続的なものにしなくてはならないことを表しています。このように、必要なデータのみの取得をおこなった場合、最後に修正されたエンティティの完全な属性のセットをロードしなければならないことだけが唯一の課題です。ほとんどのアプリケーションでは、さらに多くの読込みと書込みをおこなうため、これは実際にはトレードオフの関係です。

もちろん、エンティティBeanを使用する場合には、このような混在型のアプローチの良い面と悪い面があります。必要なデータのみを高速にフェッチできる一方で、現行のトランザクションのエンティティBeanで未コミットの変更がある場合は、値の不整合が生じてしまいます。トランザクションの前のステップで、Productの属性に対する更新が完了していないと、同じトランザクションの後続の"Fast-Lane Reader"のSQL文は、データベースから古い値を直接読み込むことになるため、現行のトランザクションでは、ビジネス・オブジェクトの最新状態が反映されません。たとえば、この値がProductの価格であれば、後続の計算で間違った値が使用され、その結果(間違った値)が他の属性値へ同様に反映されてしまいます。

ビュー・オブジェクトとエンティティ・オブジェクトの協調動作のしくみ

BC4Jフレームワークのデータアクセス・オブジェクトであるビュー・オブジェクト、およびエンティティ・オブジェクトは連携して機能し、データの不整合という問題に苦しめられることなく、"Fast-Lane Reader"のパフォーマンス上のメリットを保持します。図18のステップ1と2に示されているように、ビュー・オブジェクトはJDBCを使用してデータベースへ直接SQL問合せを送信し(ステップ1)、ベスト・パフォーマンスを得るために最適化されたJDBCコードを使用して結果を取得(ステップ2)します。BC4Jは問合せ結果の各行に対して、BC4Jが提供する汎用Row値オブジェクトの実装を使用して、結果を保持します。このようにビュー・オブジェクトは、"Transfer Object(Value Object)"パターンに対する自動的なサポートとともに、"Fast-Lane Reader"パターンの実装を提供します。

ビュー・オブジェクトがデータベースへSQLを送信し、エンティティ・メタデータに基づいて結果をキャッシュする
図18: ビュー・オブジェクトがデータベースへSQLを送信し、エンティティ・メタデータに基づいて結果をキャッシュする

各ビュー・オブジェクトに対して、メタデータを保持するXMLファイルが用意され、ここでSQL文におけるSELECTリストに含まれる(DB表の)列と関連するエンティティ・オブジェクトのもとの属性との対応付けを管理しています。図18では、このメタデータ情報が、EmployeeListビュー・オブジェクト内のSQL問合せの結合に対する点線として示されています。問合せの中のDEPTNOおよびDNAMEは、Deptエンティティ・オブジェクトのDeptnoおよびDname属性にマップされ、問合せの中のENAMESAL、およびEMPNOは、Empエンティティ・オブジェクトのEnameSal、およびEmpno属性にマップされています。

汎用ビュー・オブジェクトの実装コードはこのメタデータを使用して、問合せで取得された適切な属性の保管をもとのエンティティ・オブジェクト・インスタンスに自動的に委任します。これら、もとのエンティティ・インスタンスは、ビュー・オブジェクト間で共有されるため、複数のビューで取り出された共通データの整合性が自動的に保持されます。上記に示すように、データベースから取得された各行に対して、Deptエンティティにマップされた2つの属性は、実際にはこっそりとキャッシュ内のDeptエンティティのインスタンスに格納されます。同様に、問合せから取得され、Empエンティティにマップされた3つの属性は、Empエンティティのインスタンスのキャッシュに格納されます。問合せのいくつかの行に部門(Dept)20のデータが含まれている場合は、これらの行のそれぞれが、主キー(Deptno)が20であるようなDeptエンティティの共通インスタンスを指します。SQLの式TRUNC((SYSDATE-HIREDATE)/365)で算出されたYearsOfServiceなど、導出された属性値では、ビュー・オブジェクト自身がキャッシングを処理します。

図18では、BC4Jが、部分的なエンティティの存在を許可するエンティティ・キャッシュをサポートしていることに注意してください。DEPT表のLOC列、およびEMP表のHIREDATE列は問合せの文では使用されないため、メモリには格納されません。これは単純な例ですが、80から100個の属性を持つ大きなエンティティでは、このような部分的な取得が可能になると、非常に有効です。背後のキャッシングの詳細に関係なく、問合せ結果を、繰返し処理および更新が可能なRow値オブジェクトのコレクションとして操作できます。たとえば、ある行で従業員(Emp)の給与(Sal)の値を更新するには、curRow.setSal(3500)をコールするだけで、後はフレームワークが処理してくれます。

図19は、Row値オブジェクト属性の1つが更新される場合のステップを表しています。前述の"Fast-Lane Reader"問合せ結果のキャッシングで使用したものと同じメタデータによって、自動的な委任がおこなわれ、更新が簡単におこなわれるようになります。

ビュー・オブジェクトが更新をエンティティ・オブジェクトに委任して、検証と永続性を処理する
図19: ビュー・オブジェクトが更新をエンティティ・オブジェクトに委任して、検証と永続性を処理する

ステップ1では、開発者がsetSalを呼び出して、現在のRow値オブジェクト・インスタンスのSal属性の値を変更します。ステップ2では、ビュー・オブジェクトの行が、もとのEmpエンティティ・オブジェクトの適切なインスタンスに、setterメソッドの実施を委任します。この処理は自動でおこなわれ、ユーザーによるコーディングは必要ありません。Empエンティティ・オブジェクト・クラスにビジネス・ロジックが定義されていて、その施行の際に(この例におけるHiredateのような)未取得の属性値が要求される場合、ステップ3のようにエンティティ・オブジェクトが、主キーに基づいてすべてのエンティティ属性の補足(Fault-in)を実行することが示されています。すべてのエンティティ・オブジェクトのビジネス・ルールが成功すると、属性を内部エンティティ属性に格納しようとします。その際に、排他的ロックが有効になっている場合は、対象行がデータベースでロックされます(ステップ4)。ロックが取得されると、値が正常にエンティティ・オブジェクト・インスタンスに設定されます(ステップ5)。エンティティ・データは共有されているため、結果コレクションに、従業員番号7788のSal属性を持つRow値オブジェクトが含まれているビュー・オブジェクトは、すぐに変更を確認できます。

セッションBeanまたはそのコンテナがトランザクションをコミットする前に、"Fast-Lane Reader"のビュー・オブジェクト問合せが実行されると、前に説明したように、SQLを直接データベースへ送信する処理をおこないます。未完了のエンティティの変更はまだコミットされていないため、この問合せは、データベースの現在の値を取得します。ただし、DAO層でBC4Jデータアクセス・オブジェクトを使用する場合に注目すべき点は、問合せ結果に、現在のトランザクションのビジネス・エンティティで保留されている変更内容が正しく反映される、ということです。つまり、BC4Jを使用すると、従来の"Fast-Lane Reader"の実装において、論理的に不整合なデータを誤って操作してしまうという問題が回避されます。

これは、このセクションで説明したビュー・オブジェクトとエンティティ・オブジェクトの間の協調動作によって実現されるもので、データベースから取り出された古い値に対して、エンティティ・キャッシュがコミット前の変更内容で属性値を処理していることに起因しています。たとえば、ビュー・オブジェクトの問合せで値10を取得したとします。しかし、この値が現行のトランザクションですでに15に修正されているならば、ユーザーには15と表示されます。開発者から見たRow値オブジェクトのコレクションは、現行のトランザクション内で論理的に正しい値を反映したものという結果となります。当然、ビュー・オブジェクトの結果コレクションに新しい行が作成されると自動的に、適切なエンティティ・インスタンスも作成されます。同様に、コレクションから行を削除することは、もとのエンティティ・インスタンスのremove()コールに自動的に変換されます。新規、修正、削除といった変更処理が完了していない場合に何らかの関連(Association)がプログラムによってナビゲートされると、コードは、これらの未コミットの変更を反映した論理的に正しい状態を認識します。

最後に、トランザクションがコミットされると、新規、修正、または削除されたエンティティ・インスタンスに対するデータベースの挿入、更新、および削除が、ステップ7のように実行されます。

エンティティの結合による参照用のビュー

アプリケーションで、結合ビューの操作が必要になることがよくあります。結合ビューでは、結合内でいくつかの表が集まって参照情報を構成します。たとえば、明細を入力するフォームまたはページで、ユーザーがInventoryItemItemIdを入力すると、通常、商品の説明、価格、仕入先などの情報を検索するための手動のコード記述が必要になります。BC4Jを使用した場合は、それぞれのビュー・オブジェクトは、どのエンティティ・オブジェクトを使用しているのか認識しており、Associationを使用してエンティティ・オブジェクト間の関連付けが可能であることも認識しています。このため、BC4Jは参照情報の同期を常に保持できるようになり、タスクを実行するためのコードの記述は不要になります。

図20は、LineItemsViewの現在行のItemId属性に値が設定されたときに発生する内容を表しています。値が設定されると、フレームワークは、ビュー・オブジェクトで使用されているInventoryItemおよびSupplierエンティティ・オブジェクトから該当する参照情報を自動的に取り出します。IIおよびSから出ている斜めの線は、エンティティの参照を表しています。これは、ビューの中で更新可能なエンティティとしてではなく、参照エンティティとして定義されています。このため、ユーザーが、新しいSKU#を指定したときに追加のコーディングなしに、正しい説明(Description)、価格(Price)、および仕入先(Supplier)が自動的に検索されます。

外部キー属性の設定によって参照情報が自動的に挿入される
図20: 外部キー属性の設定によって参照情報が自動的に挿入される

BC4JにおけるDAOの他の機能

今まで見てきたように、BC4Jフレームワークの主要コンポーネントは、J2EEアプリケーションのデータアクセス・オブジェクト層を実装するための優れた生産性を実現します。これまではビュー・オブジェクトとエンティティ・オブジェクトの組合せにおける主な機能について学習しましたが、BC4Jには他にも重要な機能があります。次のセクションで、BC4JフレームワークがターゲットとするEJBセッションBeanについて学習する前に、これらの機能について簡単に説明します。

XMLデータの生成と利用

XMLは、アプリケーション統合やWebサービスにとって、アプリケーション間でビジネス情報をやりとりするための共通語です。ビジネス情報の適切な階層ビューをXMLデータグラムとして短期間に提供することは、J2EE開発者にとって今や中心的な業務のひとつとなっています。BC4Jのビュー・オブジェクトとビュー・リンクを使用すると、求められているネスト形式のビジネス・データを得ることができます。階層的なマスター/ディテールのSQL問合せを、宣言的に作成することができ、これによって、ビュー・オブジェクトでwriteXML()をコールすると、その内容の一部またはすべてがXMLとして構成されます。たとえば、前述のSlowPayingCustomersビュー・オブジェクトでwriteXML()をコールすると、例21のようなXMLデータが生成されます。

<SlowPayingCustomers>
  <SlowPayingCustomer>
   <id>101</id>
   <name>Big Al</name>
   <LatePayments>
     <LatePayment>
      <id>34112</id>
      <bill>9433</bill>
      <amount>185.23</amount>
      <payDate>2001-08-15</payDate>
      <DaysLate>14</DaysLate>
     </LatePayment>
     <LatePayment>
      <id>44563</id>
      <bill>9876</bill>
      <amount>34.76</amount>
      <payDate>2001-09-10</payDate>
      <DaysLate>9</DaysLate>
     </LatePayment>
   </LatePayments>
  </SlowPayingCustomer>
   <!-- etc. -->
</SlowPayingCustomers>
例21: ビュー・オブジェクトで writeXML()をコールして生成されたXMLデータ

さらに面白いことに、ビュー・オブジェクトはreadXML()で処理を逆にして、渡されたマルチレベルのXMLデータをすばやく取り込みます。今まで見てきたようなビュー・オブジェクトとエンティティ・オブジェクトの優れた協調動作により、ひとつのreadXML()のメソッド・コールの背後で、ビジネス・エンティティ・インスタンスの自動作成、更新、削除がおこなわれます。この処理には、エンティティ・オブジェクト・レベルで実装されているすべてのビジネス・ルールを自動的に実行することも含まれています。また、writeXMLreadXMLメソッドはXSLTの変換と連携してXMLを任意の形式で生成および取込ませることが可能です。

タイプセーフな値オブジェクト・インターフェースを使用する

汎用Row値オブジェクト・インターフェースを使用するほかに、オプションで、ベースのRowインターフェースを拡張することでEmpRowインターフェースのように誤った型変換を防ぐ(つまりタイプセーフな)値オブジェクト・インターフェースを生成することが選択できます。汎用Row値オブジェクト・インターフェースを使用した場合には、メソッド・コールは次のようになります。

curRow.setAttribute("Salary",new Number(3500));

一方、オプションのタイプセーフな値オブジェクト・インターフェースを使用した場合は、コードは次のようになります。

curRow.setSalary(new Number(3500));

タイプセーフな値オブジェクトのオプションによって、Javaコンパイラは、コードをコンパイルする際により多くのエラーを検出することができます。

カスタム・メソッドを使用して値オブジェクトをエンティティ Facadeとして使用する

ここまででわかるように、ビュー・オブジェクトによって、エンティティ・オブジェクトを簡単に制限したり結合したりでき、また、もとのエンティティ・データのサブセットや結合をおこなうタイプセーフな値オブジェクト・インターフェースを生成することもできます。さらに、オプションとして、値オブジェクト・インターフェース上でカスタム・メソッドを用意することもできます。これによって、値オブジェクトを真のエンティティ Facadeとして処理することができます。たとえば、カスタムのSlowPayingCustomersRow値オブジェクト・インターフェースで、addToWatchList()およびsuspendService()などのメソッドを作成することができます。これによって、値オブジェクトであるビュー・オブジェクト結果コレクションに対する繰り返し処理の中で、次のようにしてメソッドを起動できまます。

// slowPayers in an instance of a SlowPayingCustomers view object bean
while (slowPayers.hasNext()) {
 SlowPayingCustomersRow r = (SlowPayingCustomersRow)slowPayers.next();
 if (...) r.addToWatchList();
 else if (...) r.suspendService(10);
}
DAOコンポーネントの継承と多相コレクションの完全なサポート

多くのJ2EE開発者にとっては、エンティティBeanで継承を利用できないということは意外なことでした。実際には、EJB 2.0の仕様の中で、コンポーネントの継承はリリース2.0以降の将来的な機能として計画されています。BC4Jをデータアクセス・オブジェクト・フレームワークとして使用した場合には、UMLダイアグラムまたはウィザードのどちらを使用しても、コンポーネントの継承を最大限活用できるビジネス・エンティティをJava開発者が期待する通りの簡単な作業で作成できます。エンティティ・オブジェクトの継承階層を作成して、ビジネス構造をモデル化し、値オブジェクトの多相コレクションを簡単に操作することができます。たとえば、Peopleエンティティ・オブジェクトをベースとして、PersonDoctorPatient、およびSurgeonを表すサブ・エンティティ・オブジェクトを作成すると、すべての人物を問合せし、正確にPersonDoctorPatientSurgeonそれぞれのRow値オブジェクトを返すようなビュー・オブジェクトを簡単に生成することができます。このような多相ビュー・オブジェクトは、エンティティ・オブジェクトがユーザーによって指定された多相化識別子属性によって継承階層を作ることができることで実現されています。また、ユーザー自身でフレームワークをオーバーライドして継承階層を作成できることで、同種のオブジェクトに対するSQL問合せを最適化するということにもつながってきます。つまり、1つの表に格納されているPersonDoctorPatient、およびSurgeon行については、複雑でパフォーマンスの悪いUNION問合せをおこなう代わりに、単純なSQLで一般的な属性を簡単に問合せます。

Oracleデータベース機能に対する拡張サポート

BC4Jフレームワークのデータアクセス・オブジェクトはSQL92準拠のデータベースを使用できますが、Oracleデータベースを導入している場合は、Oracle8iまたはOracle9i データベースを使用するための特別なサポートがフレームワークに含まれます。Oracleデータベースに接続する場合には、BC4Jの一般的なデータアクセス・オブジェクトの実装は、パフォーマンスを強化するためにOracle JDBCドライバの多数の機能を利用できます。このドライバでは、以下のものをサポートしています。

共通の検証ロジックとパラメータ化されたルール

検証ルールは、エンティティ・オブジェクトで使用するために再利用可能な検証コードをカプセル化するJavaBeansとして提供されます。エンティティ・オブジェクトのJava実装クラスでJavaコードを記述しておこなう検証とは異なり、XMLデプロイメント・ディスクリプタを使用して宣言的に検証ルールをエンティティ・オブジェクトに割り当てることができます。

図22は、JDeveloperのエンティティ・オブジェクト・エディタで「検証」タブを使用して、RangeValidationBean(値の範囲検証)を、LineItemエンティティ・オブジェクトのQuantity属性に割り当てた結果を示しています。検証ルールはJavaBeanに組み込まれたデータドリブンな検証エンジンのようなものなので、通常は、使用する際にパラメータのセットが必要になります。これらのパラメータは、XMLデプロイメント・ディスクリプタの中で、検証ルールの使用を表す要素のXML属性として表記されます。したがって、値の設定とカスタマイズが簡単になります。

パラメータ化された検証ルールはエンティティ・オブジェクトのXMLデプロイメント・ディスクリプタで表現される
図22: パラメータ化された検証ルールはエンティティ・オブジェクトのXMLデプロイメント・ディスクリプタで表現される

検証ルールは、個別のエンティティ・オブジェクト属性にも、エンティティ・オブジェクト全体にも割り当てることが可能で、フレームワークは、複数の(またはゼロでも可)検証ルールのコレクションをそれぞれの場所でサポートします。検証ルールは、Javaコード内部に記述された検証とともに機能し、双方のアプローチを組み合わせて最適なものが得られるようにしています。

BC4Jには、あらかじめ代表的な検証ルールが提供されますが、BC4Jの真のパワーは、検証ルールをユーザー自身で簡単に実装できるという点にあります。つまり、BC4Jのインターフェースを実装したJavaBeansであれば良いのです。これにより、検証ルールの再利用可能なライブラリを開発チームやエンドユーザーに提供して、アプリケーションの動作を簡単で宣言的にカスタマイズできるようになりました。EJBエンティティBeanに精通している読者は、EJBエンティティBeanが宣言的なビジネス・ルールを実装するためのモデルを提供しないこと、EJBエンティティBeanでは、どんなルール基盤も「自分でやる」のが基本であると理解しているでしょう。

ドメインを使用した再利用可能な独自のデータ型の作成

実際のアプリケーションを作成していると、次のような内容をチェックするために、ちょっとしたルーチンを自分で記述することがよくあります。

ドメインは、アプリケーション内で頻繁に現れる不変のクラスで、スカラーのデータ型を表します。上記の状況を表すドメインは、それぞれTelephoneNumberCreditCardWebURL、およびFedExTrackingNumberと表すことができます。

ドメインには検証が含まれており、値が正しいことを確認するのに必要なチェックをおこないます。いったん作成すると、ドメインのインスタンスは、タイプセーフな方法でメソッド・パラメータとして自由に渡すことができます。実際にドメインを使用すると、価格の値が渡されるはずのメソッドに数量の値が渡される、といった間違いがなくなります。そういったエラーをコンパイラがすぐに通知するからです。

BC4Jでは、ドメインを、エンティティ・オブジェクト属性およびビュー・オブジェクトの属性のデータ型として、簡単に使用できるようにしています。このしくみにより、クライアントから提供される属性値について、ドメインに組み込まれている検証チェックが自動的におこなわれます。

"Factory"パターンによる多階層アプリケーションのカスタマイズ

BC4Jフレームワークは、多階層アプリケーションのカスタマイズをするために、"Factory"パターンをサポートするように設計されています。これは、スーパークラスをベースとして、実行時にはそれらのカスタマイズされたバージョン(サブクラス)を代わりに使用するものです。これによって、ベース・クラスやXMLデプロイメント・ディスクリプタの再コンパイルや変更をおこなわずに新しい属性、ビジネス・ロジック、および関連を追加するといった、既存のアプリケーションのカスタマイズが可能になります。

もちろん、JDeveloperの開発環境には、これらのフレームワーク機能を利用するための設計時の完全なサポートが用意されています。この時点で、BC4Jフレームワークで、事前に作成およびテストされている一般的なデータアクセス・オブジェクトの機能をどのように拡張していくかについて、よい考えが浮かんでいると思います。次に、EJBセッションBeanのデプロイで選択する事項について学習します。

BC4Jを使用したEJBセッションBeanのサンプル

BC4Jを使用して、次の2つの種類のEJBセッションBean Facadeを容易に作成することができます。

  1. 「サービス・セッションBean」と呼ばれるいわゆる従来のEJBセッションBean。BC4Jの汎用的なデザイン・パターン・コンポーネントを、プライベート実装の中で占有的に利用し、リモート・クライアントに対してはこれらのフレームワークを利用しません。
  2. 「AppModuleセッションBean」と呼ばれる拡張EJBセッションBean。BC4Jのデザイン・パターン・コンポーネントと機能を用いた完全なリモート・アクセスを提供します。これには、"Value Messenger"パターンが含まれており、従来問題であったEJBクライアント・ネットワークのトラフィックを低減させることができます。

EJBセッションBeanインターフェースを外部の開発者に提供しなければならない場合は、通常、プログラムでアプリケーションとインターフェースをとるために、サービス・セッションBeanを使用します。EJBセッションBeanのクライアントが、自ら所属する開発チームによって開発されたクライアント・インターフェース(JSP、サーブレット、Swingなど)であることがわかっている場合には、AppModuleセッションBeanオプションが最も適しています。

いずれの場合でも、デプロイの際にはBean 管理トランザクション(BMT)またはコンテナ管理トランザクション(CMT)のどちらを使用するかを選択することができます。EJBの仕様に記載されているように、CMT Beanとしてデプロイする場合には、BC4JはSessionSynchronizationインターフェースを実装してコンテナがトランザクションの境界を操作できるようにします。BMT Bean としてデプロイする場合は、BC4Jフレームワークは、JTA UserTransactionオブジェクトのメソッドを、コミット処理の一部として調整します。

最初に、BC4Jを利用した従来のサービス・セッションBeanと、BC4Jを利用せずに手動でコーディングしたものを比較し、次に、AppModuleセッションBeanを選択することによって、どのような機能が使用できるようになるかを説明します。アプローチの選択を随時に変更したり、両方のアプローチを試してみたりすることもできます。いずれの場合でも、選択は、配布ウィザードのリストから該当するものを選ぶだけで良いので非常に簡単におこなえます。

手動で記述したセッションBean Facadeのしくみ

手動でコーディングした"Session Facade"を使用した場合、通常は、図23に示すようなソリューションをコーディングします。Facadeクラスは、"Fast-Lane Reader"のSQL文の結果を繰り返したり、エンティティBeanのfinderをコールした結果を繰り返して、手動でコーディングした値オブジェクトのコレクションを生成します。

手動でコーディングした"Session Facade"による値オブジェクトの取得と修正の処理
図23: 手動でコーディングした"Session Facade"による値オブジェクトの取得と修正の処理

手動でコーディングした値オブジェクトの実装によっては、クライアントは、既存の値オブジェクト・インスタンスでsetterメソッドをコールするか、または値オブジェクト・クラスの新しいインスタンスを生成して、返されるデータのコピーに対して変更をおこないます。次にクライアントは、値オブジェクトのコレクションを"Session Facade"に戻して、変更を永続的なものにします。この最後のステップは、次のいずれかの方法によっておこないます。

  1. 修正されたエントリについては、エンティティBeanを検索してsetterメソッドをコールし、値オブジェクトから取り出した値を、エンティティBeanの適切な属性へ戻します。
  2. 削除されたエンティティについては、エンティティBeanを検索し、そのBeanのremove()をコールします。
  3. 新しいエントリについては、エンティティBeanのホーム・インターフェースでcreate()メソッドをコールし、修正の場合と同様のsetterメソッドをコールします。

前述の例では、Product値オブジェクトを使用しています。クライアントから渡されたProduct値オブジェクトのコレクションには、新しい商品、元のコレクションに返された商品を修正したもの、および削除されている商品が含まれている可能性があります。開発者が、クライアントによって渡されたそれぞれのProductをやみくもに処理したいのでなければ、通常は開発者が、どのインスタンスが修正、追加、削除されたかをスマートに認識できるように値オブジェクトの実装をおこなうでしょう。また、"Session Facade"のコードに属性値をやみくもに設定させたいのでなければ、値オブジェクトの実装でどの属性値が変わったのかを管理させる必要があるでしょう。

サービス・セッションBeanでBC4Jデータアクセス・オブジェクトを使用する

BC4Jアプリケーション・モジュールをサービス・セッションBeanとしてデプロイする場合には、既に学習したBC4Jデータアクセス・オブジェクトの機能を、バックエンドのエンタープライズBeanの実装で利用できます。一方、上記のように手動でコーディングした"Session Facade"の場合には、次の処理をおこなう必要があります。

図24に示されているように、値オブジェクトのコレクションを構築するためのコードでは、BC4Jビュー・オブジェクトを使用することにより、利便性や効率が改善されます。更新処理のコードでは、(エンティティBeanとほとんど同じAPIを使用して)軽量エンティティ・オブジェクトを直接使用するか、データが生成された元のビュー・オブジェクトに変更を戻して、BC4Jのビュー・オブジェクト/エンティティ・オブジェクトで変更を永続的なものにするかを選択できるといったことが可能になります。

サービス・セッションBeanのデプロイでサーバー層のBC4Jフレームワークを利用する
図24: サービス・セッションBeanのデプロイでサーバー層のBC4Jフレームワークを利用する

サービス・セッションBeanを使用するためのクライアント・コードは、手動でコーディングした"Session Facade"を使用するためのクライアント・コードと同じですが、EJB層の実装は効率よくなります。たとえば、例25は、BC4Jを利用したCatalogServiceセッションBeanを検索、作成、および使用するためのコードです。この例では、セッションBeanのリモート・インターフェースでメソッドをコールして商品リストを返し、リストを反復して各商品のName属性の値を、値の最後に「X」の文字が付加されている値に変更します。

Context ctx = new InitialContext(env);
CatalogServiceHome home = (CatalogServiceHome)ctx.lookup("CatalogService");
CatalogServiceRemote service = home.create();
Collection c = service.getProductsByStore("SFO-3");
Iterator i = c.iterator();
int row = 0; // コレクション内で繰り返し、偶数行目の商品名だけを修正する
while (i.hasNext()) {
 ProductValueObject pvo = (ProductValueObject)i.next();
 if (++row % 2 == 0) pvo.setName(pvo.getName()+"X");
}
service.updateProducts(c);
例25: EJBセッションBeanを使用するクライアント・コード

EJBインターフェースを外部の開発者に公開する場合は、自身で記述したメソッドのみがサービスのAPIになるよう、アプリケーション・モジュールをサービス・セッションBeanとしてデプロイする方法が適しています。ただし、クライアント層およびビジネス・ロジック層の両方を自分の開発チームで構築する場合は、AppModuleセッションBeanによるデプロイが機能的に最も優れています。開発者は、これらの技術から、最もニーズにあったものを見つけることができます。また、同じアプリケーション・モジュールを2つの方法でそれぞれにデプロイすることも可能です。このように、選択は常に柔軟におこなうことができます。

AppModuleセッションBeanを使用した効果的なデザイン・パターン

サービス・セッションBeanを使用すると、時間が節約され、EJB層のBean実装にメリットが提供されますが、さらに拡張AppModuleセッションBeanでは、手動でコーディングする部分さえも消し去ってしまいます。サービス・セッションBeanでは作成したリモート・メソッドのサポートのみをおこないますが、AppModuleセッションBeanは汎用BC4Jアプリケーション・モジュール・オブジェクトからリモート・コレクション機能の豊富なセットを継承できます。

これは、AppModuleセッションBeanは、ここで学習したすべてのJ2EEデザイン・パターンについてリモート処理が可能でネットワーク効率がよくなるように、万全にサポートされていることを意味します。もちろん、AppModuleセッションBeanはセッションBeanにデザイン・パターンの一般的な実装を提供できるようにして、手動コーディングの量を減少させただけのものではありません。それだけではなく、すべての機能を提供するため、およびEJBの仕様で確立されているルール内ですべてをおこなうために、高度で詳細に設計されたセッションBeanです。実際には、アプリケーション・モジュールをAppModuleセッションBeanとしてデプロイする場合は、BC4JがEJBセッションBeanを自動的に作成し、そのBeanの実装がBC4Jの汎用的な(高度なBeanの)実装を継承しています。つまり、AppModuleセッションBean Facadeは、このような優れた機能をバックボーンとして成り立っています。

BC4Jは、図26に示すように、デプロイされたセッションBeanに対して標準のEJBホームおよびリモート・インターフェースの最上部で"Value Messenger"デザイン・パターンを実装することによって、一般的なデザイン・パターンの機能に対してネットワーク効率を実現しています。BC4Jの"Value Messenger"実装では、汎用Rowオブジェクトとしてクライアントに返された値オブジェクトが、更新可能で、かつビジネス・ロジック層での元のビジネス・コンポーネントの属性値と同期化できることが保証されます。図26に示すように、値オブジェクトのコレクション(BC4Jフレームワークの用語ではRowRowset)を操作することによって、値オブジェクトを簡単に追加、修正、および削除することができます(かつ、中間層が自動的に同期化されます)。"Value Messenger"パターンの実装によって、値オブジェクトに加えられた変更を中間層のビジネス・エンティティと同期化するためのコードを記述する必要がなくなります。

BC4Jの"Value Messenger"パターンによる効率的な更新可能なリモートの値オブジェクト
図26: BC4Jの"Value Messenger"パターンによる効率的な更新可能なリモートの値オブジェクト

BC4Jは、"Value Messenger"パターンを実装することによって、アプリケーション・モジュールとビュー・オブジェクトの機能のフル・セットを拡張し、それらが実装するすべてのデザイン・パターンを含むことによって、リモート・クライアントに対するシームレスな統合を提供します。クライアントは、ページ単位での結果のナビゲート、必要に応じた行の作成、更新、削除、あるいはマスター/ディテールのコレクションの操作などを、ネットワーク上で対話することなく実行できます。図26は、クライアントが、汎用の値オブジェクトであるProduct(マスター)およびProductItem(ディテール)コレクションを特別なコードを追加せずに直接操作できることを表しています。クライアントのコレクションで値オブジェクトに加えられたすべての変更は、自動的に(かつネットワーク効率よく)、EJB層と適当な間隔で同期化されます。この図では、EJB層の実装部でも、ビュー・オブジェクトとエンティティ・オブジェクト間の調和が保持されていることも表現しています。AppModuleセッションBeanのクライアントは、ビュー・オブジェクトの結果コレクションの値オブジェクトを通じてすべてを参照し操作します。このため、クライアントAPIは非常にシンプルになります。

この機能はどのようにして実現されるのか、順を追って考えてみましょう。CatalogService AppModuleセッションBeanについて検討してみると、BC4J JNDIのInitialContext実装は、EJBサーバーによって返されたCatalogServiceHomeインターフェースをキャッシュし、代わりにCatalogWebアプリケーション・モジュールをcreate()するための汎用ApplicationModuleHomeインターフェースを返します。クライアント開発者は、サーバー側で使用したものと同じインターフェースを使用して、アプリケーション・モジュール・コンポーネントの操作を続けることができ、それだけでなく、リモート・クライアントのインターフェースの下では、スマートなプロキシ機能が実装されています。

EJBクライアントにおけるプロキシ・コードの階層は、CatalogWebアプリケーション・モジュール・インターフェース(またはそれに含まれているビュー・オブジェクト)上のすべてのアクティビティを適切なタイミングで、実際のEJBセッションBeanのリモートインターフェースへと委任します。AppModuleセッションBeanを使用した場合でもクライアント・コードは同じように見えますが、開発者が手動で値オブジェクトのコードを記述する必要はなく、サーバー側でも作成、修正、削除ロジックのための特別なコードが不要になります。例25に対応するクライアント・コードは、例27のようになります。

Context ctx = new InitialContext(env);
ApplicationModuleHome home = (ApplicationModuleHome)ctx.lookup("CatalogWeb");
CatalogWeb service = (CatalogWeb)home.create();
RowSet rs = service.getProductsByStore("SFO-3");
RowIterator i = rs.createRowSetIterator();
int row = 0; // コレクション内で繰り返し、偶数行目の商品名だけを修正する
while (i.hasNext()) {
 Row pvo = i.next();
 if (++row % 2 == 0) pvo.setAttribute("Name",pvo.getAttribute("Name")+"X");
}
// 行の更新のために、明示的にセッションBeanのメソッドを
// コールする必要はない。AppModuleインタフェースを使うことで
// 同様の機能を備えたRowSetを利用すれば良いことになる
service.updateProducts(rs);
例27: EJB(AppModule)セッションBeanを操作するクライアント・コード

ここでは、固有のセッションBeanホームの代わりに汎用ApplicationModuleHomeが使用されていることに注意してください。このため、BC4J JNDIのContext実装によって導入される、基本的なEJBセッションBeanホームおよびセッションBeanのリモート・インターフェースの上位の間接的な階層のおかげで、ローカル・キャッシングによって大量のネットワーク・トランザクションの最適化が実現されます。BC4Jの汎用デザイン・パターン・インターフェースに隠蔽されている、クライアント側の実装コードのライブラリは、EJBの参照と作成を自動的に処理し、実際のインターフェースを保持して、そのインターフェースを介して必要に応じてメソッド・コールを委任します。

前述のサービス・セッションBeanの例25で使用されていた手動コーディングによるProductValueObject値オブジェクト・クラスの代わりに、汎用RowSetリスト・インターフェースおよび汎用Row値オブジェクトも使用されています。

組み込みのデフォルト・イテレータの実装を提供すること、およびオプションでタイプセーフなカスタムの値オブジェクト・インターフェース(ここでは、ベースのRowインターフェースの代わりにProductRowを使用)を明示的にすることによって、RowSetによる RowIteratorの実装がいっそう簡単になります。このため、前述の例は、例28のように短くすることができます。

Context ctx = new InitialContext(env);
ApplicationModuleHome home = (ApplicationModuleHome)ctx.lookup("CatalogWeb");
CatalogWeb service = (CatalogWeb)home.create();
RowSet rs = service.getDepartments();
int row = 0; // コレクション内で繰り返し、偶数行目の商品名だけを修正する
while (rs.hasNext()) {
 ProductRow pvo = (ProductRow)i.next();
 if (++row % 2 == 0) pvo.setName(pvo.getName()+"X");
}
service.updateProducts(rs);
例28: カスタムのRowインターフェースを使用したタイプセーフ・チェックの改善

上記の例は、プログラムについての簡単なサンプルですが、もちろん、一般的なクライアントの機能(一般のJSPタグ・ライブラリ、コントロールにバインドされるJFC/Swingデータなど)のすべては、このリモートAppModuleセッションBeanのデプロイでシームレスに機能します。次のセクションで、BC4Jがどのようにして"Value Messanger"パターンを実装し、これらの結果を得るのかを詳細に見てみましょう。

BC4Jの"Value Messenger"パターンの実装の理解


EJBセッションBean実装の詳細について習熟していない読者は、次のセクションへ進んでもかまいません。

より高度なBC4Jの値オブジェクトの実装では、値オブジェクトの属性を保持するためにメンバ変数を持つようなJavaクラスを使用するだけでなく、クライアント側の値オブジェクト・キャッシュを自動的に利用して、ネットワーク・トラフィックの最適化と中間層の値オブジェクトの同期化を両方とも実現しています。このため、アプリケーション・モジュールをAppModuleセッションBeanとしてデプロイすると、BC4Jは、層を意識しなくても良いように、クライアント・プロキシの実装クラス層を生成します。これらのクライアント・クラスは、クライアント・プログラムでアプリケーション・データ・モデルおよびカスタム・アプリケーション・モジュールのメソッドを操作するために使用する、コンポーネント・インターフェースを実装します。このセクションでわかるように、"Value Messenger"の追加機能は今までのコーディング・スタイルを変更せずに利用できます。

BC4Jによる汎用Row値オブジェクトの実装ではクライアント側の値オブジェクト・キャッシュを使用してデータを格納するため、値オブジェクトの各属性が変更されるたびに、それをネットワーク越しに通知するといったことがありません。必要な変更がすべておこなわれると、クライアント・キャッシュ内の値オブジェクトに対して完了していない変更が、1回のラウンドトリップでまとめてEJB層に送信されます。また、ネットワーク内の同じラウンドトリップで、中間層の同期化に伴って副次的に変更されたEJB層の値が「Value Message」としてまとめて送信され、クライアント内で対応する値オブジェクトの属性がリフレッシュされます。

図29を見ると、このメカニズムが機能するしくみが明確にわかります。ここでは、単一のビュー・オブジェクトEmployeeListを使用するアプリケーション・モジュール・コンポーネントを作成したとします。このビュー・オブジェクトは、2つのエンティティ・オブジェクトDeptEmpに関連付けられています。Empエンティティ・オブジェクトの実装クラスに記述されているビジネス・ロジックで、Enameの属性値をすべて強制的に大文字にする場合について考えてみましょう。

public void setEname(String value) {
 if (value != null) value = value.toUpperCase();
 setAttributeInternal(ENAME, value);
}

また、Empエンティティの別のビジネス・ロジックで、「従業員の給与(Sal)が$3000を超える値に設定されている場合は、その従業員の休暇の制限(VacationLimit)を144時間に自動的に増やす」という業務方針を強制的に実行するとします。

図29に示されているように、ステップ1でクライアントは、値オブジェクトのSal属性を3500に設定し、Ename属性を「Steve」に設定します。このコミット前の変更は、クライアント側の値オブジェクト・キャッシュに保持されています。ステップ2では、UIナビゲーション、トランザクションの明示的なコミット、またはEJB層メソッドの明示的な起動などの操作によって、変更がまとめて中間層へ送信されます。値オブジェクトの同期化の中で、Sal属性とEname属性が、前に学習したビュー・オブジェクト/エンティティ・オブジェクトの組合せを使用して、キャッシュ内のエンティティ・オブジェクト・インスタンスに設定されます(ステップ3)。Empエンティティ・オブジェクトのビジネス・ロジックは、Enameの値を大文字に設定し、VacationLimitの値を144に設定します(ステップ4)。クライアント・キャッシュの値オブジェクトの属性に関連するすべての変更がまとめられ、ネットワークのラウンドトリップの一部としてクライアントに返されます。この例では、大文字の「STEVE」の値と、更新されたVacationLimit属性の値144が、クライアントに返されます(ステップ5)。最後にステップ6で、クライアントの値オブジェクトは、中間層の更新を「確認」し、画面またはページに最新の値を表示します。もちろん、Beanはアプリケーション・モジュール・フレームワークのベース・クラスApplicationModuleImplから"Value Messenger"パターンの実装を継承しているため、この処理をおこなうためにアプリケーション・プログラミング・コードを記述する必要はありません。

"Value Messenger"パターンによってクライアントとサーバー間の値オブジェクトを自動的に同期化する
図29: "Value Messenger"パターンによってクライアントとサーバー間の値オブジェクトを自動的に同期化する

このような"Value Messenger"パターンの実装を利用しない場合は、クライアント側の値オブジェクトには、ビジネス・データの古いコピーが保持され、データには、ルーチン・ビジネス・ロジックの実行によってEJB層でおこなわれた変更が反映されません。また、前のセクションでみたような、クライアントの値オブジェクトに加えられた変更に基づいてEJB層のビジネス・エンティティを更新するタスクは、開発者のための問題として残ったままになっています。

実際にBC4Jがどのようにして"Value Messenger"のパターンを実装するか、詳しく見てみると面白いことがわかります。カスタム・メソッドを持つMyAppというアプリケーション・モジュールを、アプリケーション・モジュールのリモート・インターフェース上で公開する場合について考えてみましょう。

// 作成されたアプリケーション・モジュール実装クラス
public class MyAppImpl {

 // リモート・アクセス可能なカスタム・メソッド
 public Integer someMethod(String x, Number y) {
  // 適切な処理...
 }

}

アプリケーション・モジュール・コンポーネントをAppModuleセッションBeanとしてデプロイする場合には、セッションBean Facadeと"Value Messenger"の実装が、図30に示すUMLクラス図のように自動的に生成されます。「Value Message」をクライアントからEJB層へ一括して送信するため、それぞれのカスタム・メソッドにはバイト配列の別のパラメータが付加されていることに注意してください。また、メソッドの同期処理の一部としてEJB層から「Value Message」をまとめて返すために、それぞれのカスタム・メソッドの戻り値がPiggybackReturnの型に変更され、実際のメソッドの戻り値をこの「搬送用(piggyback)」オブジェクトの一部として含むためのコードが生成されています。

アプリケーション・モジュールに対して自動生成された"Session Facade"と"Value Messenger"の実装
図30: アプリケーション・モジュールに対して自動生成された"Session Facade"と"Value Messenger"の実装

幸い、アプリケーション・クライアントとしては、これらの実装の詳細について認識したり注意を払ったりする必要はありません。図31に示されているように、クライアント・コードは、使用する層に依存しないMyAppインターフェースで機能します。このインターフェースは、アプリケーション・モジュールの実装クラス内のカスタム・メソッドとまったく同じ形になります。

生成されたクライアント・コンポーネント・インターフェースにより"Value Messenger"パターンの実装が隠蔽される
図31: 生成されたクライアント・コンポーネント・インターフェースにより"Value Messenger"パターンの実装が隠蔽される

BC4Jで生成された、このインターフェースを実装するクライアント・プロキシ・オブジェクトは、未完了の値オブジェクトの変更をEJB層に送信するための詳細について自動で考慮します。また、EJB層の値オブジェクト属性の変更に伴って、クライアントの値オブジェクト・キャッシュをリフレッシュする処理についても自動で考慮します。

EJB を利用しないローカル・モードでのデプロイ

今まで、BC4Jアプリケーション・モジュールをEJBセッションBean Facadeとしてデプロイする方法をいくつか見てきました。ただし、今日のJ2EEアプリケーションの多くは、ローカル・クラスでJavaコードにアクセスするサーブレットやJSPを完全にベースとしていることを忘れはいけません。つまり、J2EEアプリケーションではJ2EE Web層を利用していますが、これらのアプリケーションはまだJ2EE EJB層を容認していません。もし、あなたがEJBへ移行していない開発者だったとしても、心配はいりません。BC4Jのパワーを利用したアプリケーションでは、J2EEのデプロイ・アーキテクチャの決定が開発者に委ねられており、アプリケーションのライフサイクルを通じていつでも選択できるようになっています。

アプリケーション・モジュールをEJBセッションBeanとしてデプロイするかどうかに関係なく、BC4Jフレームワークは、優れたアーキテクチャと高い自由度を持つ「サービス Facade」を促進しています。これは、すべてのアプリケーション・モジュールは、「ローカル・モード」で(つまりシンプルなJava Beanとして)デプロイして使用し、J2EE Web層のみを対象としたアプリケーション・アーキテクチャをサポートすることも可能であることを意味しています。いつでも、Oracle9i JDeveloperのアプリケーション・モジュール・エディタを再使用することで、アプリケーションのコードを変更せずに、同じアプリケーション・モジュールをEJBセッションBeanとして再度デプロイすることができます。したがって、J2EEアプリケーションのデプロイでBC4Jフレームワークを採用することは、デザイン・パターンのアプリケーション・プログラミング・コードを大量に記述しなくてすむだけでなく、デプロイを柔軟に選択でき、アプリケーション開発チームでEJB層を採用するためのベストなタイミングを計画することができます。

BC4Jと組み合わせるクライアント・テクノロジ

コアBC4Jフレームワークは、"Model/View/Controller"アーキテクチャのモデルを実装して、データを思いどおりに操作し、必要に応じてデータを更新したり、プロセス内でビジネス・ロジックを自動的に再利用したりするためのパワフルな機能を提供します。また、アプリケーション・モジュールは、EJBセッションBean Facadeに示されるタスク特有のカスタム・メソッドを記述するのに便利な場所であるため、この機能を使用して「Controller」ロジックをアプリケーション・モジュール・クラスの中に直接記述するユーザーもいます。

いずれの場合でも、BC4Jのパワーを利用したJ2EEアプリケーションの「View」および「Controller」層を実装する場合には、非常に柔軟に処理できます。Oracle9i JDeveloperには、アプリケーションでこの部分の実装をサポートする補足的なクライアント・フレームワークがいくつか用意されています。これらのフレームワークはすべて、BC4Jアプリケーション・モジュールに対して、ビルトインのサポートをおこなうものです。BC4Jとこれらの追加のUIフレームワークを組み合わせると開発の生産性が向上します。

Web層アプリケーションのステート管理とプーリング・フレームワークの共有

J2EE Web層のステート管理は、アプリケーション開発者が直面している最も一般的で、かつ厄介な問題のひとつです。Web層のステート管理は、論理アプリケーション・タスクで、ユーザーが複数のページを参照してジョブを終了させるような場合に必要になります。未完了の処理は、完了時に1つの単位としてコミットまたはロールバックできるように、すべて「複数ページ間」で保有されています。ただし、これを実現するのは思ったよりも簡単ではありません。

たとえば、Webベースで経費報告をおこなうシステムについて考えてみましょう。新しい経費報告書をファイルするためには、複数のページにアクセスして、最終的な報告書を発行する前にいくつかの入力項目を追加、編集、およびレビューする必要があります。従来のクライアント/サーバー・アプリケーションでは、この未完了のステートが、専用のデータベース接続を使用する特別なユーザー・プロセスのメモリに格納されていました。ただし、Webアプリケーションでは多数のユーザーを追加して拡張しなければならない場合もあるため、サーバー層のプロセス、およびWebユーザーに対するデータベース接続を専用に保持しているのは現実的ではありません。

Webアプリケーションの開発者は通常、この拡張性の要件に対処するために、ステートレス・アプリケーションのアーキテクチャを選択するため、各ページの要求が終了するたびに未完了のタスクを一時的な記憶域に保存するしくみを考案する必要があります。このしくみを簡単にするために、BC4Jアプリケーション・モジュールは、未完了のステートを便利なXML形式に随時にシリアライズし、未完了の処理の「スナップショット」からすぐにこれらのステートをリフレッシュする機能をサポートしています。

また、BC4Jにはアプリケーション・モジュール・プーリング・フレームワークが付随しています。これは、組み込みの「ステート・スナップショット」機能を利用して、ページ・リクエストの間の未完了のWebユーザー・アプリケーション・ステートを自動的に管理するものです。この機能はビルトインで提供され、コーディングする必要はありません。未完了のステートは、Webファームのデプロイ構成のサポートや、さまざまなWebリスナー間でのフェイルオーバーの実現のために、セントラル・データベースにスナップショットとして保存されます。プールは、動的に割り当てられているアプリケーション・モジュール・インスタンスのセット(または、EJBセッションBeanのデプロイの場合は、リモートにデプロイされているインスタンスを参照しているクライアント)を管理し、後述する「結合性のあるステートレス」のWeb層ステート管理アプローチを実現します。

「結合性のあるステートレス」とは、多数のページ・リクエストの中でプールがWebユーザーに前のリクエストで使用したものと同じアプリケーション・モジュール・インスタンスを提供しようとすること(そのインスタンスがプール内で現在も有効で、リクエストの間に他のWebユーザーによって使用されていなければ)を意味します。同じインスタンスが有効でない場合は、別の有効なインスタンスがプールから取得され、アプリケーション・モジュールの未完了ステートが、永続しているセッション・ステートのスナップショットに基づいて復元されます。この技術はOracle E-Business Suiteの開発チームによる大規模なテストによって検証済みです。彼らは優れたパフォーマンスを提供するためにBC4Jを使用しています。

Webアプリケーションのパフォーマンスを改善するための別の方法として、ほとんど読取り専用に使用されるデータをWebユーザー間で共有することが挙げられます。アプリケーション・モジュール・プールは、選択したアプリケーション・モジュール・インスタンスを、クライアント間においてスレッドセーフな方法で共有することによって、このしくみをサポートします。このようにすると、共通の参照情報についてのデータベースへの再問い合わせがおこなわれなくなるため、アクセスがスムーズになります。

BC4Jデータ・タグを使用したデータをバインドするJSPページ

BC4Jのデータ・タグは、JSP 1.1に準拠したタグ・ライブラリで、アプリケーション・モジュールの任意のビュー・オブジェクトからデータのレンダリング、ナビゲーション、および更新を簡単におこなえるようにするものです。この技術資料の最初のセクションの例(Pet Storeデモからマスター/ディテールWebページを再構築する例)では、これらのBC4Jデータ・タグを使用しています。Oracle9i JDeveloperには、JSPページでデータ・タグを使用するための統合的なサポートが用意されています。具体的には、ページ内で入力が可能なすべての場所においてどのタグと属性を使用できるか常に認識できる「タグ・インサイト」、およびウィザードによるタグの挿入機能です。JDeveloperは、開発中にサーバーへデプロイせずにローカル環境でJSPの実行とデバッグが可能ですので、より短期間での「テスト−修正−再テスト」のサイクルを実現します。

JClientフレームワークを使用したデータをバインドするSwingフォーム

Oracle JClientフレームワークは、クライアントUIコントロールとBC4Jビュー・オブジェクト間の通信を処理するSwingモデルを実装することによって、データをバインドするSwingベースのアプリケーションやアプレットを簡単に構築できるようにします。ウィザードを使用すると、標準のSwingコントロール、JClientコントロール、サードパーティのアドイン・コントロールなど、モデルベースのコントロールによってデータバインドをおこなうフォームを簡単に生成することができます。JClientフレームワークを使用して作成されたアプリケーションは、SunのJava Web Startを使用して、高度なJavaの表示を実現する新しいWebベースデプロイのパラダイムを可能にします。Oracle9i JDeveloperには、SwingベースのUIパネルに対してレイアウトを視覚的に設計および修正するための統合的なサポートが含まれています。

Oracle XSQL Pagesを使用したXML/XSLTベースの情報公開

Oracle XSQL Pagesは、XMLによる情報をWeb上にスナップとして公開します。Oracle9i JDeveloperには、任意のBC4Jビュー・オブジェクトからXML情報を取得したり、受け取ったXMLドキュメントに基づいてビューを更新したりすることができる特別なXSQLアクション・ハンドラが付随しています。XSQLページを介して公開されたXMLページは、ターゲットXML、HTML、またはテキストベースのフォーマットへ簡単に変換できるように、XSLTスタイルシートと組み合わせることも可能です。JDeveloperにおけるXSQLページの実行およびデバッグのサポートは、前述のJSPページに対するサポートと類似しています。

Oracle UIX Webアプリケーション・フレームワーク

Oracle UIX Web アプリケーション・フレームワークは、従来は自分でコードを記述しなければならなかったさまざまな処理に対して、再利用可能なテクノロジを提供することによって、洗練された高度なWebアプリケーション(たとえば多言語対応のセルフサービスHRなど)の実装を簡単におこなえるようにします。たとえば、UIXには、Webページのレンダリング、データ・バインディング、イベント・ハンドリング、ページごとのナビゲーション、およびカスタマイズを可能にする構築済みのコンポーネントが用意されています。これによって、コンポーネント化されたページ・レイアウト要素の完全なセットをWebアプリケーションに提供して、さまざまなクライアントのタイプにおいて高度で一貫したルック・アンド・フィールを実現します。もちろん、図32に示すように、UIXには、BC4Jデータバインディングに対する組み込みのサポートも用意されているため、任意のBC4Jビュー・オブジェクトからのデータの取込みや修正も簡単におこなうことができます。

UIXフレームワークによる、ページ・レンダリング、ページ・フロー、BC4Jデータバインディング、およびその他の機能の提供
図32: UIXフレームワークによる、ページ・レンダリング、ページ・フロー、BC4Jデータバインディング、およびその他の機能の提供

Oracle UIXテクノロジは、Javaプログラムから使用することも、またUIX XMLページのテンプレートを介したり、JSPページでUIX JSPタグ・ライブラリを使用したりすることによって宣言的に使用することもできます。Oracle9i JDeveloperには、UIX XML要素とJSPタグに対する「タグ・インサイト」機能、およびレイアウト・コンポーネントの組合せがブラウザでどのように表示されるかをすぐに確認するランタイム・プレビュー機能があります。

UIX Controllerサーブレットは"Front Controller"デザイン・パターンを実装し、図33に示すようなUIXフレームワークのその他のテクノロジの使用を調整します。

Oracle UIXフレームワークによる"Front Controller"パターンと"Model/View/Controller"パターンの実装
図33: Oracle UIXフレームワークによる"Front Controller"パターンと"Model/View/Controller"パターンの実装

BC4Jを他のMVCフレームワークで使用する

ここで紹介したクライアント開発の組み込みオプションが、実際のニーズまたは開発スタイルに適さない場合には、BC4Jを使用して作成したJ2EEアプリケーションと、サードパーティのWebアプリケーション・フレームワーク、または自身で設計したフレームワークを簡単に組み合わせることができます。データバインディングを自動化するために前述のオプションで使用されているすべてのAPIは、ユーザー独自のクライアント・フレームワーク・コードで使用できるように文書化されており、利用することができます。

EJB 2.0による変更点

オラクルのBC4Jフレームワーク開発チームは、正式なJ2EEデザイン・パターンが推奨される前から、"Session Facade"アーキテクチャでのローカルな軽量データアクセス・オブジェクトの使用を推進してきました。2001年8月14日に最終的な形式で正式にリリースされたEJB2.0の仕様には、ローカル・インターフェースの概念(つまりビジネス・ロジック層の他のBeanが、リモートでメソッド・コールをおこなわずにエンティティBeanを使用できるようにすること)が導入されています。この新しいEJB仕様では、データアクセス機能を抜粋したローカル・インターフェースを持つ軽量エンティティBeanとして、データアクセス・オブジェクトを実装することができます。

EJB 2.0では、(BC4Jの軽量ローカル・エンティティ・オブジェクト・クラスに類似の)「ローカル・エンティティBean」が追加された他にも、(この資料で簡単に紹介した)BC4Jのナビゲート可能なエンティティ間の関連(Association)に類似の機能を提供するためのコンテナ管理のリレーションシップが導入されています。現在は、オラクルを含めたEJBアプリケーション・サーバー・ベンダーにとって過渡期であるため、BC4J開発チームは、Oracle9i JDeveloperのリリースの中で、EJB 1.1とEJB 2.0の両方のアプリケーション・サーバーを引き続きターゲットとしています。このため、このリリースでは軽量エンティティ・オブジェクト・クラスを保持しています。

Oracle9iAS、WebLogic、およびその他のJ2EEアプリケーション・サーバー・ベンダーは、2002年の上半期から新しいEJB 2.0の仕様に対する完全なサポートの展開を始めます。これに伴い、オラクルの軽量ローカル・エンティティ・クラスは、必要なインターフェースが軽量EJB 2.0ローカル・エンティティBeanとなるようにサポートされるようになるでしょう。現時点では、これらの拡張ではアプリケーション・コードを変更せずに新しい仕様のメリットを利用できる予定です。

このように、我々はEJB 2.0で軽量ローカル・エンティティBeanが導入されることを強く要望してきましたが、あいにく仕様の作成者は、EJB-QL問合せ言語のORDER BY文やエンティティBeanコンポーネントに対する継承のサポート、ベンダー特有の拡張を使用せずにfinderメソッドでSQL言語を使用する、といった重要な機能を仕様に取り入れる時間がなくなってしまいました。2000人以上のオラクル社内のアプリケーション開発者は、「データベースから最適なパフォーマンスを得るためのSQL言語を利用する機能が完全に取り入れられていなければ、高度なアプリケーションを開発することはできない」と何度も訴えています。オラクルは、Java Community ProcessのEJB 2.1およびJ2EE 1.4デザインの重要メンバーとして、自社のデータアクセスおよびアプリケーション構築の専門家がJ2EEプラットフォームに専念し、EJB仕様の今後のバージョンでアプリケーションがより簡単に構築できるようにサポートしていきます。

まとめ

この技術資料でさまざまな例を通じて見てきたように、BC4Jフレームワークを使用して作成されたアプリケーションは、J2EEに完全に準拠し、アプリケーション・サーバーのEJB 1.1またはEJB 2.0アーキテクチャを最適に利用できるだけではなく、すべてのJ2EEのデザイン・パターンの一般的な実装からメリットを得ることができます。BC4Jを使用すると、EJBの開発者、まだJ2EE Web層のみを対象としている開発者のいずれにとっても生産性が向上します。チーム開発でフレームワーク・ベースのアプローチがJ2EE開発者にもたらす「生産性の向上」というメリットを正しく認識し、それを享受できるようになるにつれて、BC4Jフレームワーク、およびOracle9i JDeveloperに組み込まれている設計ツールでは、BC4Jのパワーを利用するJ2EE開発チームが競争優位を確保できるよう画期的な機能を追加し続けます。

BC4J デザイン・パターン・カタログ

このセクションでは、BC4Jが実装しているJ2EEデザイン・パターンを簡単に説明します。パターンの中には、Sunの『J2EE Blueprints』ですでに認知されたものや、BC4Jがこのリストに追加したものがあります。

パターン名 説明 BC4Jにおける実装
Data Access Objects 従属オブジェクトを、エンタープライズBeanとしてではなく軽量で永続的なクラスとして実装し、データ交換時の不要なオーバーヘッドを回避します。永続データを、単一の保守が簡単なクラスにカプセル化します。 BC4Jビュー・オブジェクトは、SQL文によってデータを読み込むためのデータアクセスの実装を自動化します。BC4Jエンティティ・オブジェクトは、軽量ビジネス・エンティティの永続性の保持を自動化します。これは、EJB 1.1準拠のEJB 2.0「ローカル・エンティティBean」を実装しているようなものです。BC4Jのビュー・オブジェクトとエンティティ・オブジェクトは、協調動作して洗練された高度なアクセス・オブジェクト層を提供します。この層では、ビュー・オブジェクトを介して問合せされたデータが、追加の「アプリケーション・プログラミング」コードなしで、完全に更新できるようにします。
Model/View/Controller 同じビジネス情報をさまざまなタイプのクライアント表示が使用できるように、データと表示の役割を明確に分離します。 BC4Jのアプリケーション・モジュール・セッションBeanは、アプリケーションまたはサービスに対するアプリケーション・データ・モデルの提示を簡単にする"Model/View/Controller"アプリケーション・オブジェクトの一般的な実装を提供し、論理的な作業単位の境界を宣言的に指定しやすくします。Oracle9i JDeveloperで提供される、UI中心の追加のフレームワークとタグ・ライブラリは、開発者がView層とController層を実装する際に役に立ちます。
Session Facade セッションBeanでエンティティBeanをラッピングすることによって、エンティティBeanの効率の悪いアクセス、および取り扱いに注意を要するビジネスデータの不用意な使用を回避します。 BC4Jアプリケーション・モジュールは、サポートされている任意のデプロイモードで、自由度の高い「サービス Facade」アーキテクチャを実装するように設計されています。EJBセッションBeanとしてデプロイされている場合は、"Session Facade"パターンの実装が自動的に提供されます。
Transfer Object(Value Object) クライアント・プログラムで必要な関連属性のセットをグループ化したオブジェクトを作成し、それを一度に転送することで、ネットワークの不要なラウンドトリップを回避します。 BC4Jは、汎用Rowオブジェクトの実装を提供します。これは、クライアントがアクセスする必要のある任意の数および種類の属性の、メタデータドリブンのコンテナです。開発者は、汎用Rowインターフェースの使用、getAttribute("Price")およびsetAttribute("Quantity")のような遅延バインド型メソッドのコール、または(オプションで)OverdueOrdersRowのような早期バインド型Rowインターフェースの生成をおこない、getPrice()やsetQuantity()などのタイプセーフなメソッド・コールを可能にします。これは単純な「属性のセット」よりもスマートなもので、BC4JのRowオブジェクトが実行時に行の番号、名前、およびデータ型を返し、一般的で高度な実装のソリューションを可能にします。
Value List Handler(Page-by-Page Iterator) 大量のコレクションを表示用の単位に細分化し、不要なデータがクライアントに送信されないようにします。 BC4Jは、ビュー・オブジェクトのSQL問合せの実行によって生成された結果セットを管理する、汎用RowSetインターフェースの実装を提供します。開発者はRowSetを使用して、ページサイズ(10行など)を設定したり、問合せ結果をページサイズの単位で上下に移動したりできます。データはユーザーが本当に使用する分のみバックエンドのデータベースから取得され、クライアント層では、ネットワークを介して1回のラウンドトリップでページに必要な行が返されます。
Fast-Lane Reader JDBC APIに直接アクセスして、読取り専用データに対する不要なオーバーヘッドが発生しないようにします。このパターンでは、アプリケーションは、クライアントでわずかな属性のみが必要な場合に、主キーによってすべての属性を検索せずに、表示する必要のある属性のみを取り出すようにします。低レベルのJDBCで実行される問合せでは、エンティティBeanで表されるビジネス情報の未完了の変更は参照しないため、通常、このパターンの実装ではデータの整合性が保証されません。 BC4Jのビュー・オブジェクトは、最適なパフォーマンスを得るためにデータベースから直接データを読み込みますが、開発者は、データの一貫性について選択することができます。未コミットの変更について「更新可能にする」または「整合性を保つ」(あるいはその両方)を有効にしたい場合には、開発者は、ビジネス・データが示されているエンティティ・オブジェクトにビュー・オブジェクトを関連付ける必要があります。整合性を保つ必要がない場合は、ビュー・オブジェクトは単純に問合せを実行するだけで、特別なオーバーヘッドは発生しません。いずれの場合でも、開発者はJDBCデータアクセス・コードを記述する必要はなく、SQL文を指定するだけですみます。
Front Controller Webアプリケーションのビューの管理(ナビゲーション、テンプレート管理、セキュリティなど)を、クライアント要求の受取をおこなう1つのオブジェクトに集中します。 Oracle UIX Webアプリケーション・フレームワークのUIX Controllerサーブレットは、このデザイン・パターンを実装して、ページ・レンダリング、イベント・ハンドラ、ページ・フローなどを管理します。
Factory 外部で設定可能な情報に基づいて、特定のインターフェースまたはスーパークラスの実行用サブクラスとしてインスタンス化します。 すべてのBC4Jフレームワーク・コンポーネントはカスタマイズを容易にするために、factoryクラスによって実行用の特化されたコンポーネントとしてインスタンス化されます。
Entity Facade データの制限付きビュー、および1つ以上のビジネス・エンティティの動作を提供します。 BC4Jのビュー・オブジェクトは、1つ以上のもとになるエンティティ・オブジェクトの組合せから属性とメソッドのセットを表面化して、操作対象の単一の論理的な値オブジェクトをクライアントに供給することができます。
Value Messenger クライアントの値オブジェクト属性と、(その属性が双方向に表現する)中間層のビジネス・エンティティ情報との同期を保持します。 BC4Jの値オブジェクトの実装は、クライアント側の値オブジェクトのキャッシュと調整し、変更属性をEJB層に渡し、中間層のビジネス・ロジックの結果として発生する更新属性を受取ります。BC4Jの"Value Messenger"実装は、非同期のメッセージングを必要とせずに効果が得られるように設計されています。

FAQ

参考文献

  1. Designing Enterprise Applications with the Java™ 2 Platform, Enterprise Edition (First Edition)