別のブラウザで表示すると、JavaScriptによってこのドキュメントの表示形式が変わる場合があります。ただしドキュメントの内容に影響はありません。

UIX開発者ガイド Go to Table of Contents
目次
Go to previous page
前へ
Go to next page
次へ

4. データ・バインド

このトピックでは、ノード・ツリー自体は変更せずに、uiXMLのデータ・バインド要素およびUIX ComponentsのBoundValueインタフェースを使用して、ユーザーごとに動的コンテンツを生成するページの作成方法を説明します。また、DataObjectインタフェースを使用してデータをグループ化する方法や、DataObjectBoundValue、UIX Components BeanおよびuiXMLのページで使用可能にする方法について説明します。さらに、DataObjectListインタフェースおよび現在のDataObjectを使用した、UIX Componentsによるデータ反復処理のサポート方法についても説明します。最後に、DataObjectおよびDataObjectListをuiXMLに直接作成する方法を説明します。

ここでは、次の項目について説明します。

動的属性: BoundValue

BoundValueインタフェースは、UIX Componentsの動的ページにとって不可欠です。これは、次のようにRenderingContextが渡され、任意のJavaオブジェクトが返される1つのメソッドで構成されています。

public interface oracle.cabo.ui.data.BoundValue
{
  public Object getValue(RenderingContext context);
}

この単純なインタフェースが便利なのは、すべてのUIX Components Beanであらゆる属性値のかわりに使用できるためです。属性の値がBoundValueに設定されている場合、その属性の値を取得しようとしてもBoundValueは返されません。UIX ComponentsではBoundValue.getValue()をコールし、結果を返します。次に例を示します。

  // Remember the code to set the text of a StyledTextBean...
  styledTextBean.setText("Some text");
  // ... is really a cover for:
  styledTextBean.setAttributeValue(UIConstants.TEXT_ATTR,
                                   "Some text");

  // So you can also set a BoundValue:
  BoundValue boundValue = ...;
  styledTextBean.setAttributeValue(UIConstants.TEXT_ATTR,
                                   boundValue);

この後、表示時にStyledTextBeanのテキストを要求する場合、次のように指定します。

  text = styledTextBean.getAttributeValue(renderingContext,
                                          UIConstants.TEXT_ATTR)
  // .. is equivalent to writing:

  BoundValue boundValue = ...;
  text = boundValue.getValue(renderingContext);

setAttributeValue()BoundValueを使用して、すべての属性をデータ・バインドできることを覚えておいてください。属性のデータ・バインドのための専用のメソッドがそのBeanにあるかどうか、以前にその属性がデータ・バインドされたかどうか、あるいはその属性が必要とするJavaオブジェクトのタイプはどれかなどは関係ありません。すべての属性をデータ・バインドできます。

多くのBeanで、属性をバインドするための便利なメソッドが用意されています。StyledTextBeanのtext属性もその1つです。次のように記述できます。

  BoundValue boundValue = ...;
  styledTextBean.setTextBinding(boundValue);  

ただし、これは利便性が高いというだけです。便利なメソッドがあるかどうかにかかわらず、すべてのBeanですべての属性をバインドできます。

例: 現在の日付

BoundValueには多数の実装が用意されていますが、次にカスタムの実装について説明します。次のコードでは常に現在の日付が返されます。

  public class CurrentDate implements BoundValue
  {
    public Object getValue(RenderingContext context)
    {
      return new Date();
    }
  }

これをDateFieldBeanで使用します。

  DateFieldBean dateField = new DateFieldBean();
  dateField.setValueBinding(new CurrentDate());

上の例で生成される日付フィールドは、常に現在の日付で初期化されます。上のコードは次のコードとよく似ています。

  DateFieldBean dateField = new DateFieldBean();
  dateField.setValue(new Date());

ただし大きな違いがあります。2番目の例では日付の設定は1回です。Beanを1回だけ使用して解放する場合は、問題ありません。ただし、UIX ComponentsではBeanは何度も再利用できます。最初の例で使用したBoundValueの場合、UIX Componentsにより、ページのレンダリングのたびに日付が要求されます。このため、DateFieldBeanが作成された後でも常に正確な月日が表示されます。

重要なデータのバインドについてまだ説明していません。たとえば、uiXMLでの例を示していません。また、RenderingContextgetValue()に渡される理由も説明していません。ここで、uiXMLおよびビルトインのBoundValueについて説明します。

BoundValueとuiXML

uiXMLでは、BoundValue<boundAttribute>要素によって設定されます。この要素にはname属性があり、これによってバインドされる属性の名前が設定されます。使用するBoundValueを定義する1つの子要素もあります。

<link text="Static link text">
 <boundAttribute name="destination">
   <!-- The BoundValue goes here... -->
 </boundAttribute>
</link>

ここでは、リンクUINodeを作成しました。text属性は固定ですが、destinationにはバインド先が必要です。次に、BoundValueのUIX要素を作成して、バインド先を指定します。

<link text="Static link text">
 <boundAttribute name="destination">
   <contextProperty xmlns:ctrl="http://xmlns.oracle.com/uix/controller"
                            select="ctrl:baseURI"/>
 </boundAttribute>
</link>

これで、BoundValueの要素を定義できました。<contextProperty>によって定義されたBoundValueの種類を説明するために、さらに2つのRenderingContextメソッドを使用します。

public interface RenderingContext
{
   // ...

   public Object getProperty(String namespace, Object key);
   public void setProperty(String namespace, Object key, Object value);

   // ...
}

これらのRenderingContextメソッドを使用して、任意の数の値を格納できます。前に説明したUINodeネームスペースの場合のように、ここでも競合を回避するためにネームスペースを使用します。独自のネームスペース内にプロパティを設定すると、UIX Componentsのビルトイン・ネームスペースを上書きする心配がありません。

ここで、再び<contextProperty>の例を参照します。

<contextProperty xmlns:ctrl="http://xmlns.oracle.com/uix/controller"
                          select="ctrl:baseURI"/>

ここでは、ctrlネームスペースを定義しました。UIX ComponentsおよびuiXMLのビルトイン・サポートを備えたWebアプリケーション・フレームワーク、UIX Controller用のURLが指定されています。UIXサポートの一部として、ネームスペース内にいくつかのRenderingContextプロパティが設定されています。その1つがbaseURI、つまりサーブレットを実行するWebサーバーを示すURLです。

また、select属性も定義しました。この属性では、RenderingContextから取得するプロパティのネームスペースおよび名前を指定します。この例では、UIX ControllerのネームスペースおよびbaseURI名を要求しています。

ContextPropertyBoundValueは便利ですが、Beanに動的データを取り込む最適な方法ではありません。1つには、必要となるすべての値をRenderingContextに渡す必要があるためです。

役に立つBoundValueは他にも多数あります。たとえば、<concat>を使用すると、複数の文字列(動的文字列および静的文字列を含む)を1つの文字列に連結できます。しかし、動的データにとって最も重要なBoundValueDataBoundValueです。そのため、ここでDataObjectについて説明します。

DataObject

DataObject Javaインタフェースは、UIX ComponentsおよびuiXMLのページの汎用データソースです。DataObjectを使用して、Javaバック・エンドをUIXユーザー・インタフェースに接続します。DataObjectは、1つのメソッドのみを含む、非常に単純な新たなインタフェースです。

public interface oracle.cabo.ui.data.DataObject
{
  /**
   * Select a single value out of the DataObject.
   * @param context the current RenderingContext
   * @param select  a select key
   * @return the selected value
   */
  public Object selectValue(RenderingContext context, Object select);
}

DataObjectインタフェースは、BoundValueインタフェースによく似ていますが、RenderingContext以外のものも使用します。selectオブジェクトも使用します。つまり、多数の異なるデータに対して1つのDataObjectを要求できます。DataObjectには一連のデータが含まれています。この意味ではHashtableまたはMapによく似ていますが、DataObjectは小規模のインタフェースであり、開発者またはデータ構造に対する要件はごくわずかです。そのため、次のような任意のデータ形式に容易に適応できます。

UIXには、BC4Jへのビルトイン・バインド(「Business Components for Javaの統合」を参照)と、「より簡単な方法: Beansへのバインド」で後述するJavaBeansへのビルトイン・バインドが組み込まれています。

ほとんどの開発者は、最初にselectパラメータを目にした際にHashtableのキーを想定するでしょうが、DataObjectはそのようには機能しません。selectパラメータは次のいずれかになります。

DictionaryData

DataObjectの最も単純な実装は、DictionaryDataクラスです。データを直接DataObjectに渡すことが可能で、HashtableDataObjectに変換することもできます。

例: 

  // Build up information about an employee
  DictionaryData data = new DictionaryData();
  data.put("empName", "Joe Smith");
  data.put("manager", "Larry Ellison");
  data.put("ID", "12345");

  // or...
  Hashtable employee = _getEmployeeData(...);
  DictionaryData moreData = new DictionaryData(employee)

DictionaryDataは非常に簡単で、単純なテストケースでは有用です。ただし、乱用しないよう十分に注意してください。すべてのアプリケーションにおいて、DataObjectの独自の実装を何度も記述し、使用することになります。特に、次のようなコードには十分注意してください。

  // This is bad code;  don't do this!
  jdbc.sql.ResultSet jdbcResults = _getEmployee(...);
  DictionaryData data = new DictionaryData();
  data.put("empName", jdbcResults.getObject("empName"));
  data.put("manager", jdbcResults.getObject("manager"));
  data.put("ID",      jdbcResults.getObject("ID"));

このようなコードの問題点は、オブジェクト間で結果をコピーするために、時間とメモリーの両方が無駄に使用されることです。必要時にのみデータを取得するDataObjectのカスタムの実装を使用する方が適切です。DictionaryDataをまったく使用しないという意味ではありません。1つか2つのデータをページに追加するだけの場合には、適切な方法です。それより複雑な場合、DictionaryDataは使用しないでください。

次に、独自のDataObjectクラスの記述方法を説明します。

例: BundleDataObject

このクラスでは、JavaのResourceBundleDataObjectに変換します。このクラスは大変便利なため、UIX Componentsに含まれています。ここではその説明を通して、DataObjectを実装する際の基本的な考え方を示します。この例では、DataObjectで例外を処理する方法も示します。

  import java.util.MissingResourceException;
  import java.util.ResourceBundle;

  import oracle.cabo.ui.data.DataObject;

  public class BundleDataObject implements DataObject
  {
    public BundleDataObject(ResourceBundle bundle)
    {
      _bundle = bundle;
    }

    public Object selectValue(RenderingContext context, Object select)
    {
      try
      {
        return _bundle.getObject(select.toString());
      }
      catch (MissingResourceException e)
      {
        context.getErrorLog().logError(e);
        return null;
      }
    }

    private ResourceBundle _bundle;
  }

留意点: 

  1. ResourceBundleをラップした場合、1つのオブジェクトしか割り当てられません。しかし、DictionaryDataを作成してすべてのデータをResourceBundleから取り出した場合、さらに多数のオブジェクトを作成する必要があります。また、前述の例の場合は、実行時に実際に使用されているキーのみが取得されます。
  2. DataObjectで、例外がスローされないようにする必要があります。ここでは、MissingResourceExceptionを検出し、標準のUIX ErrorLogに記録してから、安全な値すなわちnullが返されています。

DataObjectの独自の実装は、非常に簡単に記述できます。カスタムの実装を使用することによって、アプリケーションの効率やパフォーマンスが向上します。

DataObjectRenderingContext

DataObjectの作成後、RenderingContextインタフェースに連結して、UIX Componentsのページで使用できるようにします。(前述のように、このインタフェースが、UIX Components BeanとuiXMLのページに、レンダリングに必要なコンテキストを渡します)。ここで、RenderingContextの特定のメソッドに注目します。

  public DataObject getDataObject(String namespaceURI,
                                  String localName)

このメソッドによって、UIX Components BeanまたはBoundValueでは、任意の数のDataObjectへのアクセスが可能になります。UIXでは通常、これらのDataObjectは2つの文字列で識別されます。WebサイトのURLに基づく1つ以上のネームスペースと、他のコードと競合しない任意のローカル名を使用します。

DataObjectへのバインド: DataBoundValue

ここで、DataObjectRenderingContextに渡す方法からは一度離れ、DataObjectからデータを取得してUINodeまたはuiXMLに渡す方法を説明します。ここで、DataBoundValueクラスや<dataObject>要素が渡されます。

uiXMLでは、<dataObject>要素を使用して、RenderingContextDataObjectを取得できます。この要素は、<contextProperty>のように<boundAttribute>要素の内部で使用できます。次の例は、前に示したものと同じuiXMLの例ですが、DataObjectを使用している点のみが異なります。

<link text="Static link text">
 <boundAttribute name="destination">
   <dataObject xmlns:myNS="http://www.example.org/"
                         source="myNS:dataObjName"
                         select="url"/>
 </boundAttribute>
</link>

留意点: 

  1. <dataObject>要素を使用しています。
  2. カスタムのネームスペース宣言("xmlns:myNS=...")を追加しています。
  3. source属性によって、取得するDataObjectを指定しています。コロンの前の部分でネームスペースを、後の部分で名前を指定します。ここでは、ネームスペースとしてhttp://www.example.org、名前としてdataObjNameを使用しています。
  4. select属性によって、選択キー、つまりDataObject.selectValue()に渡す値を指定しています。

したがって、このuiXMLは、レンダリング時に次のJavaコードを実行していることと同じです。

  // ...
  DataObject dataObject = context.getDataObject("http://www.example.org",
                                                "dataObjName");
  Object destination = dataObject.selectValue("url");

  //  ...

次に、<boundAttribute><dataObject>を一緒に使用するための簡略フォームについて説明します。このフォームにより、このような一般的な例を簡単に入力できるようになります。

UIX Componentsでは、DataBoundValueクラスによってこの機能が提供されます。

  LinkBean bean = new LinkBean();
  DataBoundValue destinationBinding =
    new DataBoundValue(_EXAMPLE_NAMESPACE,
                       "dataObjName",
                       "url");
  bean.setDestinationBinding(destinationBinding);

  // ...

  static private final String _EXAMPLE_NAMESPACE =
     "http://www.example.org";

DataBoundValueが中心的に使用されるため、DataBoundValueを作成する次のような便利なメソッドがBeanに用意されています。

  LinkBean bean = new LinkBean();
  bean.setDestinationBinding(_EXAMPLE_NAMESPACE
                             "dataObjName",
                             "url");

DataObjectの登録: DataProvider

ここまでで、独自のDataObjectを記述する方法、それらをRenderingContextから取得する方法、およびそれらを属性にバインドする方法を学びました。この他に説明が必要なものは、DataObjectRenderingContextに渡す方法です。この方法では別のインタフェース、DataProviderインタフェースを使用します。これは小規模なインタフェースです。

  public interface DataProvider
  {
    public DataObject getDataObject(
      RenderingContext context,
      String           namespace,
      String           name);

    // ... methods omitted
  }

まだ説明していないメソッドがいくつかありますが、中でもgetDataObjectは重要なメソッドです。これはDataObjectを取得するためにRenderingContextでコールするメソッドです。このメソッドにはネームスペースと名前が渡されるため、各DataProviderでは様々な多くのDataObjectを提供できます。最後に、DataProviderRenderingContextに連結する方法です。これは、もう1つのメソッドを使用するだけで可能です。

  public interface RenderingContext
  {
    // ...

    public void addDataProvider(DataProvider provider);

    // ...
  }

DataProviderRenderingContextにする回数で何度でもこのメソッドをコールできます(実際には、すべてのDataProviderTableDataProviderの中に入れ、それをCachingDataProviderの中に入れる方が適切です。ただし、この方法については後で説明します)。これで準備ができました。次の要約の後、全工程を示す例に進みます。図4-1を参照してください。

図4-1: DataObject、DataProvider、RenderingContextおよびUINodeの関係

Logical representation of relationships among DataObjects, DataProviders, RenderingContexts, and UINodes

  1. DataProviderDataObjectのコレクションです。
  2. DataObjectは任意のデータのコレクションです。
  3. RenderingContextは、DataProviderからDataObjectを取得します。
  4. DataBoundValueオブジェクト(またはuiXMLのdataObject要素)により、すべてがUINode属性に連結されます。

ここでは、いくつかの例を示します。まず、UIX Componentsの例で、ビルトインのDataProvider実装を紹介します。次に、uiXMLの例で、データ・バインドの設定に使用するuiXMLの要素をいくつか紹介します。

DataProviderの例: UIX Components

非常に単純なページを作成します。リンクが1つだけあり、コンテンツを1つのDataObjectにデータ・バインドします。すべてをJSPで表示します。最初にいくつかの定数を定義します。

public class DataDemo
{
  // ...
  private static final String _DEMO_NAMESPACE   = "http://www.example.org/";
  private static final String _DATA_OBJECT_NAME = "TextData";

  private static final Object _TEXT_KEY = "textKey";
  private static final Object _DESTINATION_KEY = "urlKey";
}

ここで、UIX Componentsのノードを作成する関数を記述します。

public class DataDemo
{
  static public UINode getPage()
  {
    // An ultra-simple example - we'll create a single link:
    LinkBean link = new LinkBean();
    link.setTextBinding(
      new DataBoundValue(_DEMO_NAMESPACE,
                         _DATA_OBJECT_NAME,
                         _TEXT_KEY)
                       );

    // Or, we can use the built-in DataBoundValue convenience methods
    link.setDestinationBinding(_DEMO_NAMESPACE,
                               _DATA_OBJECT_NAME,
                               _DESTINATION_KEY);

    // And put the link inside of a "BodyBean" (see below)
    BodyBean body = new BodyBean();
    body.addIndexedChild(link);
    return body;
  }

  // ...
}

このコードは、前に作成したコードによく似ています。リンクのBeanを作成し、2つの属性をデータ・オブジェクトにバインドしています。新たに追加したのはBodyBeanのみです。これはデータ・バインドの一部ではありません。HTMLの<body>タグの作成に常に使用されるUIX Components Beanです。

ここで、DataProviderを設定する関数を記述します。

注意: これは非常に基本的な例です。データがハードコードされており、基本のDictionaryDataクラスを使用してそのデータを格納しています。ただし、レンダリング時にJSPのPageContextを渡すことができます。つまり、そのコンテキストを使用してここで必要なすべての状態を取得できます。さらに、DataProviderおよびDataObjectにデータが要求される際、RenderingContextはその両方に渡されます。また、ServletRequestHttpSessionなどの一般的なサーブレット・オブジェクトをRenderingContextから取得できるため、すべてのデータを事前にロードしておく必要はありません。データが実際に要求されるまで待機できます。

public class DataDemo
{
  // ...

  static public DataProvider getDataProvider()
  {
    DataObject data = _getData();

    // And put it in a DataProvider
    TableDataProvider provider = new TableDataProvider();
    provider.put(_DEMO_NAMESPACE, _DATA_OBJECT_NAME, data);

    return provider;
  }

  static private DataObject _getData()
  {
    // Build up a DataObject
    DictionaryData data = new DictionaryData();
    data.put(_TEXT_KEY, "Shameless promotion!");
    data.put(_DESTINATION_KEY, "http://www.oracle.com");

    return data;
  }


  // ...
}

ここで使用したTableDataProviderに注意してください。このクラスでは、DataObjectやその他のDataProviderを、ネームスペースおよび名前によって分割します。

最後に、レンダリングを行うJSPを記述します。

<%@ page contentType="text/html" %>
<%@ page import='oracle.cabo.ui.ServletRenderingContext'%>
<%@ page import='oracle.cabo.ui.beans.StyleSheetBean'%>
<%@ page import='oracle.cabo.ui.data.DataProvider'%>
<%@ page import='yourpackage.DataDemo'%>
<html>
<head>
<%
  // Create a rendering context
  ServletRenderingContext rContext =
    new ServletRenderingContext(pageContext);

  // Include the stylesheet that UIX Components needs
  StyleSheetBean.sharedInstance().render(rContext);
%>
</head>
<%
  // Get the data provider, and attach it to the
  // rendering context
  DataProvider provider = DataDemo.getDataProvider();
  rContext.addDataProvider(provider);

  DataDemo.getPage().render(rContext);
%>
</html>

この例を実行すると、「Shameless promotion!」という語句が表示されます。これは、オラクル社のWebサイトにリンクされています。

この例では、別のBean、StyleSheetBeanも使用しています。このBeanでは、UIX Componentsのすべてのページで必要とされるスタイルシートを自動的に追加します。このBeanの使用による、1行のコードでの高性能処理やカスタマイズの実現については、後半のトピックで説明します。ここでは、このBeanがHTMLのすべての<head>に属することを理解してください。

必要な作業はこれですべてです。1つの単純なリンクをレンダリングするには少々過度な処理です。ただし、データベースとの対話、ローカライズされたテキストの提供、またはローカライズされたデータに関するその他のソースの提供を可能にする、汎用のDataProviderが使用されています。

DataProviderの例: uiXML

ここで、uiXMLを使用して同じ例を作成します。必要なXMLを次に示します。

<dataScope xmlns="http://xmlns.oracle.com/uix/ui"
           xmlns:demo="http://www.example.org/">
 <provider>
   <data name="demo:linkData">
     <method class="yourpackage.DataDemo" method="getLinkData"/>
   </data>
 </provider>

 <contents>
   <link>
     <boundAttribute name="text">
       <dataObject source="demo:linkData" select="textKey"/>
     </boundAttribute>
     <boundAttribute name="destination">
       <dataObject source="demo:linkData" select="urlKey"/>
     </boundAttribute>
   </link>
 </contents>
</dataScope>

例からわかるように、<boundAttribute>要素によってコードが煩雑になっています。この例を簡潔にする簡略フォームについては後述します。ここでは、この例の新しい要素を見ていきます。

  1. <dataScope>:
    この要素を使用すると、UIXのページ内にDataProviderを追加できます。すべてのDataProvider<provider>要素の内部に定義され、それらのプロバイダを使用できるすべてのUIは、<contents>要素の内部に定義されます。
  2. <data name="...">
    <data>要素では、DataObjectによって提供されるデータのネームスペースと名前を定義します。その内部には、データの取得方法を指定する1つの子要素を追加します。各DataObjectについて必ずしも1つの<data>を指定する必要はありません。<data>には、ローカル名としてワイルドカードのアスタリスク(name="demo:*"など)を指定できます。
  3. <method class="..." method="..."/>
    最後に、<method>で、データを取得する1つの方法を指定しています。このケースでは、イントロスペクションを使用してJavaメソッドからデータを取得します。<method>要素が機能するためには、Javaメソッドを次のように指定する必要があります。
    public class YourClassName
    {
      static public DataObject yourMethodName(
        RenderingContext context, String namespace, String name)
      {
        ...
      }
    }

ここで、このuiXMLでコールするJavaメソッドを記述します。DataDemoの例に戻ります。

public class DataDemo
{
  static public DataObject getLinkData(
    RenderingContext context, String namespace, String name)
  {
    return _getData();
  }
}

これで終了です。次に、データ・バインドのための簡略フォームを紹介します。これにより、DataObjectへのバインドを非常に簡単に行えるようになります。

DataObjectとuiXML

DataObjectへのバインドは頻繁に行われるため、<boundAttribute>を使用するよりはるかに簡潔な特殊構文をuiXMLに追加しました。前に示したコード例を次に示します。

<dataScope xmlns="http://xmlns.oracle.com/uix/ui"
           xmlns:demo="http://www.example.org/">
 <provider>
    ...
 </provider>

 <contents>
   <link>
     <boundAttribute name="text">
       <dataObject source="demo:linkData" select="textKey"/>
     </boundAttribute>
     <boundAttribute name="destination">
       <dataObject source="demo:linkData" select="urlKey"/>
     </boundAttribute>
   </link>
 </contents>
</dataScope>

次に、簡潔な構文を示します。

<dataScope xmlns="http://xmlns.oracle.com/uix/ui"
           xmlns:data="http://xmlns.oracle.com/uix/ui"
           xmlns:demo="http://www.example.org/">
 <provider>
    ...
 </provider>

 <contents>
   <link data:text="textKey@demo:linkData"
               data:destination="urlKey@demo:linkData"/>
 </contents>
</dataScope>

このように簡潔になりました。変更点は次のとおりです。

  1. 新規のネームスペースが<dataScope>要素のdataに定義され、UIX Componentsのネームスペースが指定されています。すべてのネームスペース宣言と同様に、この宣言も任意の要素に定義できます。通常は、最初の要素に宣言を定義するのが最も簡単です。
  2. <boundAttribute>要素は2つともなくなりました。
  3. data:text属性を追加しました。属性をdataネームスペースに入れることにより、uiXMLでは、その属性がデータ・バインドされることを認識できます。
    この構文では、demoネームスペースのlinkDataという名前のDataObjectの選択値として、textKeyを使用するよう指定されています。
  4. 同じ構文にdata:destination属性も追加しました。textKey値ではなく、urlKey値を取得しています。

これと同じパターンを使用して、任意の属性を任意のDataObjectにバインドできます。

"data:attributeName=key@namespace:name".

文字列の連結、またはRenderingContextプロパティへのバインドなどのような、複雑なことはできません。その場合は、<boundAttribute>を使用してください。

ただし、この簡略フォームにはいくつかの種類があります(詳細は「uiXMLでの複雑なデータ・バインド」を参照してください)。keyは省略できます(アットマーク(@)は省略不可)。省略した場合、uiXMLではselectValue(...)をコールするのではなく、DataObject自体を属性として使用します。反対に、キーのみを指定することもできます。その構文の意味を、次のセクションで説明します。

データ反復処理

ここまでで、BoundValueDataObjectを使用して、動的データを含むページを設定する方法を学びました。ただし、DataObjectインタフェースでは、一連のプロパティがサポートされるだけです。表形式のデータ、またはJava配列のようなその他のデータを表示する場合、これは非常に不便なインタフェースです。そのような場合に、DataObjectListインタフェースが使用されます。

DataObjectList

このトピックで説明する最後の新規インタフェースは、単純なインタフェースです。

  public interface DataObjectList
  {
    public int getLength();
    public DataObject getItem(int index);
  }

DataObjectListは、実際にはDataObject配列です(実際には配列を使用しないため、DataObjectListを不変に保つことができます。何かを設定するメソッドはないため、DataObjectを作成するか、DataObjectインスタンスを再利用することになります)。

ArrayDataSetクラスは、DataObjectListの単純で便利な実装です。DictionaryDataのように使用方法が単純ですが、すべてのDataObjectを事前に作成する必要があります。

  DataObject[] array = new DataObject[10];
  for (int i = 0; i < 10; i++)
  {
    array[i] = ...;
  }

  ArrayDataSet list = new ArrayDataSet(array);

DataObjectListの独自の実装を記述する必要があることを覚えておいてください。

DataObjectListへのバインド: 現在のDataObjectの指定

ここで、データをDataObjectListから取り出して、ページに戻す方法について説明します。それには、現在のDataObjectを指定することが必要です。

すべてのデータ・フレームワークで、データの反復処理がサポートされています。これはJavaではforループに相当します。データベースではカーソルに相当します。UIX Componentsでは、現在のDataObjectの指定がそれに当たります。

このトピックで前述したRenderingContext.getDataObjectメソッドを思い出してください。これまで触れていませんが、DataObjectを取得するためのメソッドがもう1つあります。

public interface RenderingContext
{
  // ..

  /**
   * Get a DataObject by namespace and name.
   */
  public DataObject getDataObject(String namespaceURI,
                                  String localName)

  /**
   * Get the "current" DataObject.
   */
  public DataObject getCurrentDataObject();

  // ..
}

この新しいメソッドgetCurrentDataObject()は、現在のDataObjectを返します。これは、データベース・カーソルに相当するUIX Componentsのメソッドであり、DataObjectListへのバインドに使用します。

Javaで現在のDataObjectからデータを取得するには、1つの引数を使用するDataBoundValueの特別なコンストラクタ、または1つの引数を使用するBeanの便利なメソッドを使用します。

  StyledTextBean textBean = new StyledTextBean();
  textBean.setAttributeValue(UIConstants.TEXT_ATTR,
                             new DataBoundValue(TEXT_KEY));

  // ... OR, EASIER ...

  textBean.setTextBinding(TEXT_KEY);

uiXMLでは、次のソースをまとめて省略できます。

<styledText>
 <boundAttribute name="text">
   <dataObject select="textKey"/>
 </boundAttribute>
</styledText>

<!-- OR -->

<!-- Note that there's no "@" sign here -->
<styledText data:text="textKey"/>

ここまでの内容をすべて使用して、TableBeanのためのDataObjectListを作成してみましょう。

public class DataDemo
{
  // ...

  static private DataObjectList _getDataForTable()
  {
    DataObject[] rows = new DataObject[5];
    for (int i = 0; i < rows.length; i++)
    {
      DictionaryData row = new DictionaryData();
      row.put(_TEXT_KEY, "Row " + i);
      row.put(_TEXT_2_KEY, "Column 2, row " + i);
      rows[i] = row;

    }

    return new ArrayDataSet(rows);
  }

  // ...

  private static final Object _TEXT_2_KEY = "text2Key";
}

ここでも、例を単純にするためにハードコードされたデータを使用しましたが、実際のアプリケーションではより多くのデータを処理できます。ここで、このDataObjectListを表に入れる必要があります。次のようにコールできます。

  TableBean table = new TableBean();
  table.setTableData(_getDataForTable());

ただし、このように設定されたデータは動的にならないため、リクエストごとにTableBeanを再利用することはできません。データを動的にするために、前のデモで使用したものと同じDataObjectにデータを追加できます。新しいキーも必要になります。また、コードを変更して、TableBeanをページに追加します。

public class DataDemo
{
  // ...
  static public UINode getPage()
  {
    // ... old code ...

    // And now, let's create the table
    TableBean table = new TableBean();
    table.setTableDataBinding(_DEMO_NAMESPACE,
                              _DATA_OBJECT_NAME,
                              _TABLE_DATA_KEY);
    body.addIndexedChild(table);

    return body;
  }


  static private DataObject _getData()
  {
    // Build up a DataObject
    DictionaryData data = new DictionaryData();

    // ... old code ...

    // And now, add the DataObjectList
    data.put(_TABLE_DATA_KEY, _getDataForTable());
    return data;
  }


  // ...

  static private final Object _TABLE_DATA_KEY = "tableKey";
}

前に示した例のように、DataProviderを使用して、DataObjectRenderingContextに追加されます。このコードを変更する必要はありません。また、表データのバインドに以前と同じコードを使用していることに注意してください。tableDataは単に別の属性であり、その他の要素は変わっていません。

ここには多数のオブジェクトがあります。作成したオブジェクトについて見なおします。

  1. DataProviderは1つのDataObjectを提供します。
  2. このDataObjectには、2つのテキストと1つのDataObjectListが含まれます。
  3. DataObjectListには、5つのDataObjectが含まれます。
  4. それぞれのDataObjectには2つのテキストが含まれます。

現在のDataObjectの指定が機能している例はまだ示していませんが、表に列を追加した後で、現在のDataObjectの指定を使用する必要が生じます。TableBeanの各列は、索引付けされた子によって表されます。addIndexedChild()を3回コールすると、表に3つの列が作成されます。あるいは、uiXMLでは、<contents>内部の各要素が1つの列に対応します。これらの各列は、行ごとに子要素をスタンプとして一度使用することによってレンダリングされます。

異なる方法でレンダリングされる各要素を行ごとに取得する点が特長です。これは、現在のDataObjectの指定を使用することで可能になります。表で最初の行をレンダリングする際、RenderingContext.getCurrentDataObject()により、tableDataのDataObjectListの最初のDataObjectが返されます。2行目のレンダリング時には、getCurrentDataObject()は2番目のDataObjectを返します。以下同じ処理が繰り返されます。

ここで表に2つの列を追加します。1つは通常のテキスト列、もう1つは赤色の列です(この例では、StyleSheetBeanで提供されるスタイルの1つである、ビルトインのOraErrorText CSSスタイルを使用しています)。

public class DataDemo
{
  // ...
  static public UINode getPage()
  {
    // ... old code ...

    // And now, let's create the table
    TableBean table = new TableBean();
    table.setTableDataBinding(...);

    // Create the first column
    StyledTextBean firstColumn = new StyledTextBean();
    // Use _TEXT_KEY for the data of the first column
    firstColumn.setTextBinding(_TEXT_KEY);
    // Add it to the table
    table.addIndexedChild(firstColumn);

    // Create the second column, using _TEXT_2_KEY
    StyledTextBean secondColumn = new StyledTextBean();
    secondColumn.setStyleClass("OraErrorText");
    secondColumn.setTextBinding(_TEXT_2_KEY);
    table.addIndexedChild(secondColumn);

    // ...
  }

  // ...
}

または、uiXMLでは次のようになります。

<table data:tableData="tableKey@demo:linkData">
 <contents>
   <styledText data:text="textKey"/>
   <styledText styleClass="OraErrorText" data:text="text2Key"/>
 </contents>
</table>

これで終了です。ここで、このレンダリング方法について説明します。表では、外枠や背景の設定など多くの処理が実行されます。その後で最初のセルがレンダリングされます。

行0、列0については、表は最初のStyledTextBeanを使用します。RenderingContext.getCurrentDataObject()によって、DataObjectListの最初のDataObjectが返されます。これには、「Row 0」および「Column 2, row 0」のエントリがあります。StyledTextBeanにより、テキストであるDataBoundValueが要求されます。現在のDataObjectが取得され、_TEXT_KEYが要求されます。_TEXT_KEYは「Row 0」を返し、そのセルにレンダリングされます。

次に、行0、列1が処理されます。同じ、現在指定されているDataObjectが有効ですが、もう1つのStyledTextBeanがレンダリングを行います。これは_TEXT_2_KEYを要求し、「Column 2, row 0」をレンダリングします。

次は行1です。最初のStyledTextBeanに戻りますが、ここではすでにDataObjectListの次のDataObjectに移動しています。そのため、RenderingContext.getCurrentDataObject()は異なるDataObjectを返します。この行の最初の列には「Row 1」、2番目の列には「Column 2, row 1」が入ります。

表全体についてこの処理が繰り返されます。これは単純な例です。列ヘッダーや行ヘッダー、あるいは表で実行可能な多数の処理は追加していません。また、編集不可の普通のテキストのみを使用しています。ただし、これらはすべて同じ方法で機能します。表の行ごとにレンダリング方法を変える場合、現在指定されているDataObjectに、その変更内容を示す情報が指定されている必要があります。

たとえば、各行にリンクを指定し、それぞれの宛先が異なるとします。この場合は簡単です。LinkBeanまたは<link>要素を使用してdestination属性を現在指定されているDataObjectにデータ・バインドし、各行の宛先を含むようにDataObjectListを更新します。UIX Componentsでは、すべてについてデータ・バインドが可能なため、各行が異なるようにできます。

uiXMLでのDataObjectおよびDataObjectListの作成

uiXML内にDataObjectDataObjectListの両方を直接定義できます。ページが実際のデータソースに連結される前にダミー・データを作成できるため、非常に便利です。また、一部のBeanでDataObjectを直接使用する場合にも有用です。たとえば、<tree>ではデータ・モデルとしてDataObjectを使用します。

まず、DataProviderの新しい要素、<inline>を紹介します。

<dataScope xmlns="http://xmlns.oracle.com/uix/ui"
           xmlns:demo="http://www.example.org/">
 <provider>
   <data name="demo:linkData">
     <inline>
        .... data object is in here ...
     </inline>
   </data>
 </provider>

 <contents>
    ...
 </contents>
</dataScope>
この要素は、前述した<method>要素に似た動作をしますが、Javaコードはコールしません。uiXML内でデータを直接定義します。

<inline>のすべての属性によって、作成されるDataObject内にキーと値のペアが定義されます。次に例を示します。

<inline url="http://example" text="Inline text">
</inline>
上の例では、2つの値を持つDataObjectを定義しています。1つのキーはurl、もう1つのキーはtextです。簡単に定義できます。

ただし、子要素を使用し、サブ値としてDataObjectおよびDataObjectListを追加することもできます。要素の名前は、子のDataObjectまたはDataObjectListが格納されるキーです。同じ要素名が複数ある場合は、それらの要素がまとまってDataObjectListを定義します。1つしかない場合、DataObjectと単一要素のDataObjectListの両方が定義されます。前述のように、これらは単なるインタフェースです。1つのJavaオブジェクトで両方を実装できます。次の例でわかりやすく示します。

<inline foo="Inline text">
 <stuff random="Stuff text"/>

 <row text="First row"/>
 <row text="Second row"/>
</inline>
この例には、3つのオブジェクトを含むDataObjectがあります。 非常に簡単です。ここで表の例に戻ります。今回は、インライン・データのみを使用します。
<dataScope>
 <provider>
   <data name="demo:inlineData">
     <inline>
       <tableKey textKey="Row 0" text2Key="Column 2, Row 0"/>
       <tableKey textKey="Row 1" text2Key="Column 2, Row 1"/>
        ... etc ...
       <tableKey textKey="Row 4" text2Key="Column 2, Row 4"/>
     </inline>
   </data>
 </provider>

 <contents>
   <table data:tableData="tableKey@demo:inlineData">
     <contents>
       <styledText data:text="textKey"/>
       <styledText styleClass="OraErrorText" data:text="text2Key"/>
     </contents>
   </table>
 </contents>

</dataScope>

uiXMLでの複雑なデータ・バインド

「DataObjectとuiXML」のセクションでは、データ・バインドの簡略フォーム、つまりdata:attributeName="key@name"について学びました。ここで、keyはテキスト・キーであり、nameDataObjectを参照しています(namenamespace:localnameという形式で指定)。このセクションでは、キーとデータ・オブジェクトをネストして、さらに複雑なデータ・バインドを生成する方法を説明します。最初に、次のフォームを使用して、ネストされたキー・データ・バインドを生成します。

data:attributeName="keyN@...@key2@key1@name"

この場合、この属性の値を計算する際に、nameで参照されるDataObjectについてkey1が使用され、別のDataObjectが生成されます。2番目のDataObjectにはkey2が使用され、3番目のDataObjectが生成されます。このプロセスは、keyNが使用され、この属性の最終値が生成されるまで続きます。すべてのDataObjectの値自体がDataObjectであることが必要です。最後のキーkeyNの値のみは、(属性が必要とするタイプであれば)DataObjectでなくてもかまいません。ネストされたキー・バインドの例を次に示します。

<dataScope>
 <provider>
   <data name="families">
     <inline>
       <Smith members="4">
         <address number="2255" street="37th Ave" city="San Francisco"
            state="CA" zip="94116" />
       </Smith>
       <Jones members="3">
         <address number="500" street="Oracle Parkway" city="Redwood Shores"
           state="CA" zip="94065" />
       </Jones>
     </inline>
   </data>
 </provider>
 <contents>
   <messageTextInput prompt="The Smiths live in"
        data:text="city@address@Smith@families" /> <!--San Francisco-->
   <messageTextInput prompt="The Jones's live in"
        data:text="city@address@Jones@families" /> <!--Redwood Shores-->
 </contents>
</dataScope>

上の例では、最初のmessageTextInputで「The Smiths live in San Francisco」が生成され、2番目で「The Jones's live in Redwood Shores」が生成されます。

ネストされたデータ・オブジェクト・バインドを生成するためにカッコも使用できます。次の例について考えてみます。

data:attributeName="(key1@name1)@name2"

上の例では、name1(で参照されるDataObject)でkey1が使用されてキーを生成し、そのキーがname2で使用されてこの属性の最終値を生成しています。これは、キー自体がデータ・バインドされる例です。データ・オブジェクトの一部をデータ・バインドすることもできます。次の2つの例は同じ意味です。

<!-- These two are equivalent -->
data:attributeName="key2@(key1@name)"
data:attributeName="key2@key1@name"

次に示すのは、前の例で作成されたfamiliesデータ構造を使用したカッコの例です。これによって、「The Jones family has 3 members.」が生成されます。

<dataScope>
 <provider>
   <data name="families">
      ... as above ...
   </data>
   <data name="selection">
     <inline current="Jones" />
   </data>
 </provider>
 <contents>
    The<link data:text="current@selection"/> family
    has<link data:text="members@(current@selection)@families" />
    members.
 </contents>
</dataScope>

カッコは、次の例のようにネストすることもできます。

data:attributeName="((key1@name1)@name2)@name3"

この例では、key1name1で使用されてキーを生成し、そのキーがname2で使用され、最終値を計算するname3に対する別のキーを生成します。

アットマーク(@)の前後のテキストはオプションです。キーを省略すると、次の例のようにDataObject自体が返されます。

<!-- The following attribute is bound to the data object itself -->
data:attributeName="@name"

データ・オブジェクト名の一部を省略すると、次の例のように、現在のデータ・オブジェクトでキーが使用されます(アットマーク(@)が指定されない場合のデフォルト)。

<!-- These keys are used with the current data object  -->
data:attributeName="key@"
data:attributeName="key"

アットマーク(@)が指定されない場合、暗黙的にアットマーク(@)があるものと仮定されます(上の例の2番目のバインドの場合)。次の例で、この詳細を示します。

<!-- These are different bindings  -->
data:attributeName="key@name"
data:attributeName="(key@name)"

上の例の2つのバインドは同じではありません。2番目のバインドには暗黙のアットマーク(@)があり、data:attributeName="(key@name)@"と記述することもできます。2番目のバインドでは、keyをデータ・オブジェクトnameに適用し返される値が、現在のデータ・オブジェクトのキーとして使用されます。

キーおよびデータ・オブジェクト名には一定の制限があります。たとえば、キーのテキストにはアットマーク(@)を使用できません。uiXMLの将来のバージョンでは、このような文字のエスケープが可能になり、使用できるようになる予定です。

より簡単な方法: Beansへのバインド

DataObjectDataObjectListは非常に柔軟ですが、多くの開発者はJavaBeansによる開発に慣れています。いつでも使用できる、記述済の多数のJavaBeansを持っている場合もあるでしょう。このような資産をUIXの使用時にあきらめる必要はありません。UIXはデフォルトでJavaBeansをサポートしているため、コード行を記述する必要はほとんどないためです。

JavaBeansを使用して、最初の例を書き直してみましょう。まず最初に、Java側を書き直します。

public class DataDemo
{
  static public Object getLinkBean(
    RenderingContext context,
    String           namespace,
    String           name)
  {
    return new LinkDataBean("http://www.oracle.com",
                            "Shameless bean promotion!");
  }
}

public class LinkDataBean
{
  public LinkDataBean(String url, String text)
  {
    _url = url;
    _text = text;
  }

  public String getUrl()
  {
    return _url;
  }

  public String getText()
  {
    return _text;
  }

  private String _url;
  private String _text;
}

この例で興味深いのはいくつかの点のみです。新規のgetLinkBean()メソッドを記述しましたが、これはDataObjectを返しません。<method>要素では、使用するメソッドがDataObjectを返す必要があると言われていたことを思い出してください。これは今や、必須ではありません(以前は必須でした)。使用するメソッドはどのオブジェクトを返してもよく、そのオブジェクトをDataObjectに変換する処理が最大限に機能します。また、LinkDataBeanには引数を持たないコンストラクタがないため、これは技術的にもJavaBeansではないことに気付いたかもしれません。(Beansそのものを作成する必要はないので、これは無視してもかまいません。)完全なJavaBeansもサポートされていますが、それよりもはるかに柔軟性を持っているのです。すぐに詳しく説明しますが、その前にXMLの例を見てみましょう。

<dataScope xmlns="http://xmlns.oracle.com/uix/ui"
           xmlns:data="http://xmlns.oracle.com/uix/ui"
           xmlns:demo="http://www.example.org/">
 <provider>
   <data name="demo:linkData">
     <method class="yourpackage.DataDemo" method="getLinkBean"/>
   </data>
 </provider>

 <contents>
   <link data:text="text@demo:linkData"
               data:destination="url@demo:linkData"/>
 </contents>
</dataScope>

変更点はほとんどありません。<method>要素はgetLinkBean()メソッドを参照するようになりました。キーとしてtextKeyおよびurlKeyを使用するかわりに、textおよびurlに変更しました。このため、BeanメソッドをgetUrlKey()およびgetTextKey()と指定する必要がなくなりました。DataObjectを参照する1コード行は、もういらなくなりました。

前述したように、使用するクラスは完全なJavaBeansである必要はありませんが、完全な方がより好ましい状態です。次のような状況はすべて自動的に処理されます。

  1. クラスが、引数を持たないコンストラクタや、publicコンストラクタさえまったく持つ必要がない。
  2. クラスが、setterメソッドを持つ必要がない(getterメソッドのみ留意される)。
  3. メソッドだけではなく、アクセス可能なpublicフィールドもサポートされるため、LinkDataBeanを次のように記述することも可能です。
    public class LinkDataBean
    {
      public LinkDataBean(String url, String text)
      {
        this.url = url;
        this.text = text;
      }
    
      public String url;
      public String text;
    }

    これは通常、独自のJavaBeansでは良い設計と言えませんが、特別なアダプタがなくてもjava.awt.Pointのようなレガシー・クラスをサポートできることを意味しています。

  4. JavaBeansではメソッドだけでなく、クラス自体がpublicになることが必要です。良い設計の原則でこれらのクラスをpublicにしないように推奨される場合があるため、この制限事項には少々問題があります。UIXの内部変換で次の代替方法をサポートします。

最後に、java.util.Mapを実装するオブジェクトにもすべて自動的に適応します。JDK 1.2以上ではjava.util.HashtableクラスはMapを実装するため、ユーザーはHashtablesを返すことが可能で、それらは自動的にDataObjectへラップされます。

配列およびその他へのバインド

UIXでは、DataObjectListの処理も単純化されています。オブジェクト配列およびjava.util.Listの双方とも、自動的にDataObjectListに適合されます。(JDK 1.2でHashtableMapを実装するように、VectorListを実装するため、そのクラスも同様にサポートされます。)

配列およびListDataObjectを含む必要はありませんが、それは可能です。かわりに、JavaBeans、MapまたはDataObjectを含むこともできます。特に秩序立てて考えない場合は、いずれか1つが3つすべてを含むことも可能です。

JavaBeans、配列およびその他に対するUIXサポートは、すべて再帰的です。これまでに学んだ複雑なデータ・バインド構文は、DataObjectを返すDataObjectに適用されたように、他のJavaBeansを返すJavaBeansにも同じように適用されます。

常にJavaBeansを使用しない理由

このように簡単な方法があるのならば、なぜDataObjectおよびDataObjectListの説明に長い時間をかけてきたのでしょうか。DataObjectはUIXでデータ用に使用される実際の概念です。第一に、Java APIを使用してBeansを作成する場合、メソッドの取得対象であるDataObjectを使用する必要があります。(いくつかのパラグラフで説明されるように、依然としてBeanAdapterUtilsを使用して手動でJavaBeansをDataObjectに変換することもできます。)

DataObjectはJavaBeansよりはるかに柔軟でもあります。JavaBeansでは、キーの完全セットをコンパイル時に知っておく必要があり、そのキーはすべて有効なメソッド名である必要があります。つまり、実行時に動的に状態が変化するBC4JまたはJDBCの行に対してバインドする単一のJavaBeansを記述することは不可能ですが、DataObjectを使用すれば簡単です。このため、JavaBeansを使用してすべてのデータを記述することも可能ですが、そうしない可能性があります。DataObjectの使用方法を知らなくても全アプリケーションを記述することはできますが、実際の動作を知っておいた方が賢明です。

また、これらのJavaBeansに対するビルトイン・サポートによるオーバーヘッドがまったくないわけではありません。パフォーマンス上のコストがかかります。最初に、DataObjectを実装するアダプタ・オブジェクトを作成する必要があります。これらは軽量であるとは言え、オブジェクト作成は可能ならば避けた方が無難です。次に、イントロスペクション(実行時におけるメソッドの動的な検出およびコール)は本質的に動作が遅くなります。このプロセスの最もコストが高い部分(Methodオブジェクトの検出)のキャッシュについては快適な動作が提供されますが、Methodオブジェクトへのコールについては依然として、手動コーディングされたDataObjectにおけるコンパイル済メソッド・コールよりも動作が遅くなります。

ただし、「早まった最適化は諸悪の根源である(Premature optimization is the root of all evil)」というプログラミングについての有名な格言を思い出してください。手動コーディングされたDataObjectがより速いとしても、DataObjectの記述に時間を費やすべきということにはなりません。適度に使用されていれば、JavaBeansの使用がパフォーマンスに重大な影響を与える理由はありません。本質的に動作が遅いJavaBeans(データベースにアクセスする必要がある場合など)については、アダプタの追加オーバーヘッドは少なくほとんど問題になりません。ただしプロファイリングを実行して、Beansの1つの適合オーバーヘッドがパフォーマンスに重大な影響を与えていると判断した場合は、オブジェクト作成およびイントロスペクション・オーバーヘッドの両方の問題に取り組むことができます。

まずは、必要になるたびにアダプタ・オブジェクトを作成することを避けます。UIXで自動的にアダプタを作成するのではなく、手動で作成してその結果をキャッシュします。BeanAdapterUtils.getAdapter()を使用すると手動でアダプタを作成できます。次に例を示します。

public class DataDemo
{
  static public Object getLinkBean(
    RenderingContext context,
    String           namespace,
    String           name)
  {
    return _sAdapter;
  }

  static private LinkDataBean _sInstance =
       new LinkDataBean("http://www.oracle.com",
                        "Shameless bean promotion!");

  // Create a static adapter:
  static private DataObject _sAdapter;
  static
  {
    try
    {
       _sAdapter = BeanAdapterUtils.getAdapter(_sInstance);
    }
    catch (InstantiationException ie) { }
    catch (IllegalAccessException iae) { }
  }
}

これは、Beanの存続期間が長い場合のみ有効です。Beanが1つのリクエストのみで使用されている場合、アダプタのキャッシュはそれほど有効ではありません。ただしそれがセッション・レベルまたはアプリケーション・レベルのBeanの場合、これはちょっとしたことですが簡単なパフォーマンス向上につながります。

次に、独自のDataObjectアダプタを記述して、イントロスペクションの本質的なパフォーマンスの問題に取り組みます。もしくはより効果的な方法として、アダプタを自動的に記述できます。UIXには、コンパイル済JavaBeansを取得しDataObjectアダプタを自動的に記述する、BuildBeanDOAdapterというJavaベースのツールが組み込まれています。コマンドラインで次のように入力します。

  java oracle.cabo.ui.tools.BuildBeanDOAdapter yourpackage.LinkDataBean

ここで生成されるコードすべては列記しませんが、次に注目すべき部分を示します。selectValue()コードが例のLinkDataBeanに対して生成されています。

  public Object selectValue(RenderingContext context, Object select)
  {
    LinkDataBean instance = _instance;
    if (instance == null)
      return null;

    try
    {
      if ("url".equals(select))
        return instance.getUrl();
      if ("text".equals(select))
        return instance.getText();
    }
    catch (Exception e)
    {
      context.getErrorLog().logError(e);
    }
    return null;
  }

これはイントロスペクションよりもはるかに速い動作です。最新のベンチマーク(Windows 2000上のJava 1.3.0)では、20から30倍速いとされています。ただし、数百ものプロパティがある大規模なJavaBeansでは、String.equals()に対するこれらのコールは重大な問題となり、この最適化が実際にはパフォーマンスにとって有害となる可能性があります。これらのケースのため、アダプタ構築ツールは"-fast"コマンドライン・オプションをサポートしています。結果として生じたクラスはStringハッシュコードを効果的に使用して、selectValue()のスピードを基本イントロスペクションよりも速いレベルまで再び引き上げます。どちらの方法でアダプタ・クラスを構築しても、これらのアダプタは、booleanおよびintの戻り値をBooleanおよびIntegerのオブジェクトに変換するような一般的なイントロスペクションよりもはるかにスムーズでもあります。

これらのアダプタ・クラスを一度作成すると、データ・バインド・コードを変更してこれらのアダプタを明示的に作成できます。

public class DataDemo
{
  static public Object getLinkBean(
    RenderingContext context,
    String           namespace,
    String           name)
  {
    LinkDataBean  bean = ...;
    return new LinkDataBeanDataObject(bean);
  }
}

ただし、ここまでのすべての手順を確実にこなしていくことを確認するのは冗長な(エラーも起こりやすい)作業です。代替案としてアダプタを、静的なregisterAdapter()コールによってBeanAdapterUtilsに直接登録できます。このコールを初期化コードに追加すると、残りのコードは以前のようにBeanインスタンスを続けて返すことができます。

public class DataDemo
{
  static public Object getLinkBean(
    RenderingContext context,
    String           namespace,
    String           name)
  {
    // Under the covers, UIX will use LinkDataBeanDataObject to
    // adapt this bean, but we don't to know that here!
    return new LinkDataBean(...);
  }

  // Register the adapter once
  static
  {
    LinkDataBeanDataObject.registerAdapter();
  }
}