ヘッダーをスキップ
Oracle Application Development Framework開発者ガイド
10g(10.1.3.0)
B40012-02
  目次
目次
索引
索引

戻る
戻る
 
次へ
次へ
 

3 アプリケーション・サービスの作成と使用

この章では、JDeveloperでアプリケーション・サービスを作成して使用する方法について説明します。

この章の内容は次のとおりです。

3.1 ビジネス・サービスの概要

アプリケーションのモデル部分を開発する場合、ビジネス・サービス、セッション・ファサード実装用のEJBセッションBean、およびデータ・コントロールを通じた機能公開のために、TopLinkを使用してPOJO(Plain Old Java Objects)を永続化することをお薦めします。Oracle JDeveloperには、モデル・プロジェクトを簡単かつ迅速に作成できる複数のウィザードが付属しています。

TopLink ADFの使用方法の詳細は、第19章「TopLinkに関する高度なトピック」を参照してください。

Oracle TopLinkの詳細は、『Oracle TopLink開発者ガイド』およびOracle TopLinkのJavadocを参照してください。


ヒント:

ほとんどの開発チームでは、ソース・コントロール管理(SCM)のための独自の手順、ポリシー、およびSCMシステムのトランザクション(または作業ユニット)の構成要素に関する共通の見解を保持しています。ポリシーが存在しない場合は、複数の論理的な変更を1つのトランザクションにグループ化する必要があります。また、変更をチーム内の他のメンバーと共有する場合、自分の加えた変更をコミットする必要もあります。一般的に、変更を正常にコンパイルできない場合や、変更用に作成されたユニット・テストに失敗した場合は、それらの変更をコミットしないことをお薦めします。

3.2 EJBセッションBeanによるサービスの実装

セッションBeanは、ビジネス・レイヤーの機能をクライアントに公開します。


注意:

TopLinkエンティティのメソッドをビジネス・サービスとして直接公開することもできますが、この方法はWebアプリケーションのベスト・プラクティスではありません。このモデルは、基本的なCRUD機能に対応しますが、ビジネス・レイヤー・オブジェクト間の対話を含む単純な操作の場合にも、複雑で扱いにくいカスタム・コードを必要とします。

セッションBeanは、J2EEデザイン・パターンのセッション・ファサードを実装する際に最もよく使用されます。セッション・ファサードは、データを集約し、それらのデータをモデル・レイヤーを通じてアプリケーションに提供するセッションBeanです。セッション・ファサードには、エンティティにアクセスするメソッドと、サービスをクライアントに公開するメソッドが含まれます。セッションBeanでは、コンテナを通じてトランザクション・コンテキストが保持されるため、基本的なCRUD機能が自動的にサポートされます。

図3-1 セッション・ファサードの機能

この図は、セッション・ファサードの機能を示しています。

3.2.1 セッションBeanの作成方法

セッションBeanを作成するには、セッションBean作成ウィザードを使用します。このウィザードは、「新規ギャラリ」の「Business Tier」カテゴリから起動できます。

セッションBean作成ウィザードでは、EJBバージョン、ステートフルまたはステートレス・セッション、リモートまたはローカル・インタフェース(あるいはその両方)、コンテナ管理またはBean管理のトランザクション(CMTまたはBMT)、セッション・ファサード・メソッドの実装方法の選択など、様々なオプションが提供されます。TopLinkプロジェクト用のセッションBeanを作成する場合は、EJB 3.0バージョンのセッションBeanとステートレス・セッションを選択する必要があります。また、コンテナ管理のトランザクション(CMT)も選択する必要があります(Bean管理のトランザクション(BMT)は、このマニュアルの対象範囲外です)。セッションBean作成ウィザードのその他のオプションは、後続の項で説明します。

3.2.1.1 リモート・インタフェースとローカル・インタフェース

必要なインタフェースのタイプは、クライアントに応じて変化します。クライアントが同じ仮想マシン(VM)上で稼働している場合、通常はローカル・インタフェースが最適です。クライアントが別のVM上で稼働している場合、リモート・インタフェースが必要です。ほとんどのWebアプリケーション(JSF、JSPおよびサーブレット)では、同じVMでクライアントとサービスが実行されるため、ローカル・インタフェースを選択するのが最適です。別のVMで実行されるJavaクライアント(ADF Swing)には、リモート・インタフェースが必要です。

3.2.1.2 セッション・ファサード・メソッドの生成

セッション・ファサードには、トランザクションの中核となるCRUDメソッドと、エンティティにアクセスするメソッドが含まれます。セッション・ファサード・メソッドを生成するには、セッションBean作成ウィザードの「セッション・ファサード・メソッドの生成」チェック・ボックスを選択し、次のページで生成するメソッドを指定します。JDeveloperでは、プロジェクト内のすべてのエンティティが自動的に検出されるため、セッション・ファサード・メソッドを作成する対象のエンティティおよびメソッドを選択できます。

同じプロジェクトのすべてのエンティティにセッション・ファサード・メソッドを作成できます。これは、テスト目的としては役に立つ可能性がありますが、一般的に単一のセッションBeanには多すぎます。通常、セッションBeanは、特定のタスクに特化されるため、そのタスクに不要な情報は格納しません。ツリー・コントロールを使用して、生成するメソッドを明示的に選択してください。

図3-2 セッション・ファサード・メソッドの選択

この図は、セッション・ファサード・メソッドのダイアログを示しています。

3.2.2 セッションBean作成時の処理

セッションBeanクラスには、セッション全体に適用されるフィールドおよびサービス・メソッドが含まれます。セッションBeanを作成すると、Beanクラスと、ローカル・インタフェースまたはリモート・インタフェース(あるいはその両方)に対応する個別ファイルが自動的に生成されます。リモート・インタフェースはセッションBeanの名前(SRAdminFacade.javaなど)となり、BeanクラスはそれにBean.javaが、ローカル・インタフェースはそれにLocal.javaが追加された名前となります。インタフェース・ファイルは、直接変更する必要がないため、アプリケーション・ナビゲータに表示されません。インタフェース・ファイルを参照するには、システム・ナビゲータまたは構造ウィンドウを使用します。

例3-1 SRAdminFacade.javaインタフェース

package oracle.srdemo.model;
import java.util.List;
import javax.ejb.Local;
import oracle.srdemo.model.entities.ExpertiseArea;
import oracle.srdemo.model.entities.Product;
import oracle.srdemo.model.entities.User;
import oracle.toplink.sessions.Session;

@Local
public interface SRAdminFacade {
  Object mergeEntity(Object entity);
  Object persistEntity(Object entity);
  Object refreshEntity(Object entity);

  void removeEntity(Object entity);

  List<ExpertiseArea> findExpertiseByUserId(Integer userIdParam);

  ExpertiseArea createExpertiseArea(Product product, User user, Integer prodId,
                                    Integer userId, String expertiseLevel,
                                    String notes);

  Product createProduct(Integer prodId, String name, String image,
                        String description);

  List<User> findAllStaffWithOpenAssignments();

  User createUser(Integer userId, String userRole, String email,
                  String firstName, String lastName, String streetAddress,
                  String city, String stateProvince, String postalCode,
                  String countryId);

  void updateStaffSkills(Integer userId, List<Integer> prodIds);
}

例3-2 SRAdminFacadeBean.java Beanクラス

package oracle.srdemo.model;

import java.util.ArrayList;
import java.util.List;
import java.util.Vector;
import javax.ejb.Stateless;
import oracle.srdemo.model.entities.ExpertiseArea;
import oracle.srdemo.model.entities.Product;
import oracle.srdemo.model.entities.User;
import oracle.toplink.sessions.Session;
import oracle.toplink.sessions.UnitOfWork;
import oracle.toplink.util.SessionFactory;

@Stateless(name="SRAdminFacade")
public class SRAdminFacadeBean implements SRAdminFacade {
  private SessionFactory sessionFactory;

  public SRAdminFacadeBean() {
    this.sessionFactory =
        new SessionFactory("META-INF/sessions.xml", "SRDemo");
  }

  /**
   * Constructor used during testing to use a local connection
   * @param sessionName
   */
  public SRAdminFacadeBean(String sessionName) {
    this.sessionFactory =
        new SessionFactory("META-INF/sessions.xml", sessionName);

  }

  public Object mergeEntity(Object entity) {
    UnitOfWork uow = getSessionFactory().acquireUnitOfWork();
    Object workingCopy = uow.readObject(entity);
    if (workingCopy == null)
      throw new RuntimeException("Could not find entity to update");
    uow.deepMergeClone(entity);
    uow.commit();

    return workingCopy;
  }

  public Object persistEntity(Object entity) {
    UnitOfWork uow = getSessionFactory().acquireUnitOfWork();
    Object newInstance = uow.registerNewObject(entity);
    uow.commit();

    return newInstance;
  }

  public Object refreshEntity(Object entity) {
    Session session = getSessionFactory().acquireUnitOfWork();
    Object refreshedEntity = session.refreshObject(entity);
    session.release();

    return refreshedEntity;
  }

  public void removeEntity(Object entity) {
    UnitOfWork uow = getSessionFactory().acquireUnitOfWork();
    Object workingCopy = uow.readObject(entity);
    if (workingCopy == null)
      throw new RuntimeException("Could not find entity to update");
    uow.deleteObject(workingCopy);
    uow.commit();
  }

  private SessionFactory getSessionFactory() {
    return this.sessionFactory;
  }

  public List<ExpertiseArea> findExpertiseByUserId(Integer userIdParam) {
    List<ExpertiseArea> result = null;

    if (userIdParam != null){
      Session session = getSessionFactory().acquireSession();
      Vector params = new Vector(1);
      params.add(userIdParam);
      result = (List<ExpertiseArea>)session.executeQuery("findExpertiseByUserId",
ExpertiseArea.class, params);
      session.release();
    }

    return result;
  }

  public ExpertiseArea createExpertiseArea(Product product, User user,
                                           Integer prodId, Integer userId,
                                           String expertiseLevel,
                                           String notes) {
    UnitOfWork uow = getSessionFactory().acquireUnitOfWork();
    ExpertiseArea newInstance = (ExpertiseArea)uow.newInstance(ExpertiseArea.class);

    if (product == null) {
        product = (Product)uow.executeQuery("findProductById", Product.class, prodId);
    }

    if (user == null){
        user = (User)uow.executeQuery("findUserById", User.class, userId);
    }

    newInstance.setProduct(product);
    newInstance.setUser(user);
    newInstance.setProdId(prodId);
    newInstance.setUserId(userId);
    newInstance.setExpertiseLevel(expertiseLevel);
    newInstance.setNotes(notes);
    uow.commit();

    return newInstance;
  }

  public Product createProduct(Integer prodId, String name, String image,
                               String description) {
    UnitOfWork uow = getSessionFactory().acquireUnitOfWork();
    Product newInstance = (Product)uow.newInstance(Product.class);
    newInstance.setProdId(prodId);
    newInstance.setName(name);
    newInstance.setImage(image);
    newInstance.setDescription(description);
    uow.commit();

    return newInstance;
  }

  public List<User> findAllStaffWithOpenAssignments() {
    Session session = getSessionFactory().acquireSession();
    List<User> result =
      (List<User>)session.executeQuery("findAllStaffWithOpenAssignments", User.class);
      session.release();
    return result;
  }

  public User createUser(Integer userId, String userRole, String email,
                         String firstName, String lastName,
                         String streetAddress, String city,
                         String stateProvince, String postalCode,
                         String countryId) {
    UnitOfWork uow = getSessionFactory().acquireUnitOfWork();
    User newInstance = (User)uow.newInstance(User.class);
    newInstance.setUserId(userId);
    newInstance.setUserRole(userRole);
    newInstance.setEmail(email);
    newInstance.setFirstName(firstName);
    newInstance.setLastName(lastName);
    newInstance.setStreetAddress(streetAddress);
    newInstance.setCity(city);
    newInstance.setStateProvince(stateProvince);
    newInstance.setPostalCode(postalCode);
    newInstance.setCountryId(countryId);
    uow.commit();

    return newInstance;
  }

  public void updateStaffSkills(Integer userId, List<Integer> prodIds) {
    List<Integer> currentSkills;

    if (userId != null) {
        List<ExpertiseArea> currentExpertiseList =  findExpertiseByUserId(userId);
        currentSkills = new ArrayList(currentExpertiseList.size());

        //Look for deletions
        for(ExpertiseArea expertise:currentExpertiseList){
            Integer prodId = expertise.getProdId();
            currentSkills.add(prodId);

            if (!prodIds.contains(prodId)){
                removeEntity(expertise);
            }
        }

        //Look for additions
        for (Integer newSkillProdId: prodIds){
            if(!currentSkills.contains(newSkillProdId)){
                //need to add
                this.createExpertiseArea(null, null,newSkillProdId,userId,"Qualified",null);
            }
        }
    }
  }
}

3.2.3 セッションBean作成時の留意事項

通常は、アプリケーションのすべての論理ユニットに対応する1つのセッション・ファサードを作成します。ただし、インスタンスの役割に応じて広い範囲でタスクを定義することも可能です(管理者クライアント操作のためのセッション・ファサードと顧客クライアント操作のためのセッション・ファサードを個別に作成するなど)。複数のセッション・ファサードを作成して名前を付けることにより、UI開発が促進されるため、特定のタスク専用のセッション・ファサードを作成してそのタスクを説明する名前を使用することは、推奨される方法です。

セッション・ファサード・メソッドを生成すると、デフォルトで各エンティティにfindAll()メソッドが作成されます。このメソッドを作成しない場合は、「セッション・ファサード・オプション」ページのツリー・コントロールでこのメソッドの選択を解除します。

セッション・ファサード・メソッドを作成または編集する場合、TopLinkエンティティとEJBエンティティの両方を選択することはできません。プロジェクトがTopLinkエンティティ用に有効化されている場合、セッション・ファサード・メソッドとして使用できるのは、TopLinkエンティティのみです。1つのセッション・ファサードでのTopLinkエンティティとEJBエンティティの統合は、将来のリリースでサポートされる予定です。

3.2.4 新規エンティティによる既存のセッションBeanの更新方法

新規のセッションBeanは、ウィザードを使用していつでも作成できます。ただし、カスタムの実装コードが含まれる既存のセッションBeanを、新規の永続データ・オブジェクトまたはメソッドで更新することも可能です。

既存のセッションBeanを更新するには、そのセッションBeanを右クリックし、「セッション・ファサードの編集」を選択します。「セッション・ファサード・オプション」ダイアログを使用して、公開するエンティティおよびメソッドを選択します。ただし、新規エンティティを作成した場合、「セッション・ファサード・オプション」ダイアログには、同じプロジェクトの新規エンティティは表示されますが、異なるプロジェクトのエンティティは表示されません。

3.3 データベース表にマップするクラスの作成

TopLinkマップ(.mwpファイル)には、データベース表をJavaクラスとして表現するのに必要な情報が含まれます。このデータを作成するには、TopLinkマップの作成ウィザードまたはマッピング・エディタを使用するか、JavaとTopLink APIを使用して手動でファイルを記述します。

この情報(メタデータ)を使用して、構成情報を実行時環境に渡します。実行時環境では、この情報を永続エンティティ(JavaオブジェクトまたはEJBエンティティBean)およびTopLink APIで記述されたコードと組み合せて、アプリケーション機能を実行します。

図3-3 TopLinkメタデータ

これは、TopLinkメタデータの相互作用の図です。

ディスクリプタ

ディスクリプタには、Javaクラスとデータソース表現の関連を記述します。ディスクリプタにより、データ・モデル・レベルでオブジェクト・クラスがデータソースと関連付けられます。たとえば、永続クラスの属性は、データベース列にマップされます。

TopLinkでは、特定クラスのインスタンスがデータソースでどのように表現されるかを記述した情報を格納するために、ディスクリプタを使用します(3.4項「表へのクラスのマッピング」を参照)。ほとんどのディスクリプタ情報は、TopLinkで定義して、実行時にプロジェクトのXMLファイルから読み取ることができます。

永続クラス

TopLinkのデータベース・セッションにディスクリプタを登録するクラスは、永続クラスと呼ばれます。TopLinkでは、データベースに格納されたprivateまたはprotected属性に対して、永続クラスでpublicアクセッサ・メソッドを提供する必要はありません。

3.3.1 クラスの作成方法

データベース表から自動的にJavaクラスを作成するには、表からのJavaオブジェクトの作成ウィザードを使用します。このウィザードでは、次のものを作成できます。

  • 各表のJavaクラス

  • TopLinkマップ

  • 各表の列にマップされた属性

図3-4 表からのJavaオブジェクトの作成ウィザード

表からのJavaオブジェクトの作成ウィザードの初期画面

初期JavaクラスとTopLinkマッピングを作成したら、マッピング・エディタを使用して情報をカスタマイズします。詳細は、Oracle JDeveloperのオンライン・ヘルプを参照してください。

3.3.2 クラス作成時の処理

表からのJavaオブジェクトの作成ウィザードが完了すると、JDeveloperによってTopLinkマップが作成され、プロジェクトに追加されます。

図3-5 ナビゲーション・ウィンドウ

ナビゲーション・ウィンドウ

このウィザードでは、(データベースの構造と関連の定義に従って)各Java属性のTopLinkディスクリプタおよびマッピングも作成されます。

図3-6 構造ウィンドウ

構造ウィンドウ

3.3.3 留意事項

データベース表からJavaクラスを作成した後で、生成されたTopLinkディスクリプタおよびマッピングを変更できます。この項には、次の内容に関する情報が含まれます。

3.3.3.1 ディスクリプタと別のデータベース表の関連付け

表からのJavaオブジェクトの作成ウィザードにより、TopLinkディスクリプタと特定のデータベース表が関連付けられます。

マッピング・エディタの「複数表の情報」タブ(図3-7を参照)を使用して、修正メソッドをディスクリプタに関連付けます。

図3-7 「複数表の情報」タブの例

「複数表の情報」タブの例

3.3.3.2 修正メソッドの使用

実行時にディスクリプタがロードされるときにコールされるようJavaのstaticメソッドを関連付けることができます。このメソッドにより、ディスクリプタのJavaコードAPIを介して、実行時のディスクリプタ・インスタンスを修正できます。このメソッドを使用すると、TopLinkで現在サポートされていない高度な構成オプションを作成できます。

このJavaメソッドには、次の要件があります。

  • public staticメソッドとする必要があります。

  • oracle.toplink.descriptors.ClassDescriptor型の単一のパラメータを使用する必要があります。

マッピング・エディタの「ロード後」タブ(図3-8を参照)を使用して、修正メソッドをディスクリプタに関連付けます。

図3-8 「ロード後」タブの例

「ロード後」タブの例

3.3.3.3 生成コードの変更

表からのJavaオブジェクトの作成ウィザードを使用すると、Oracle JDeveloperにより、Javaクラスの基本コードが自動的に生成されます。

例3-3 生成されるJavaクラスの例

package mypackage;
  import java.util.ArrayList;
  import java.util.List;

public class Address {
  /**Map employeeCollection <-> mypackage.Employee
   * @associates <{mypackage.Employee}>
   */

  private List employeeCollection;
  private Long addressId;
  private String pCode;
...

3.4 表へのクラスのマッピング

TopLinkの最も優れた機能の1つとして、オブジェクト表現とデータソース固有の表現の間でデータを変換できることがあげられます。マッピングと呼ばれるこの変換は、TopLinkプロジェクトの中心機能です。

1つのマッピングは、ドメイン・オブジェクトの単一のデータ・メンバーに対応します。マッピングにより、オブジェクト・データ・メンバーがデータソース表現に関連付けられ、オブジェクトとデータソース間で双方向変換を実行するための手段が定義されます。

TopLinkでは、オブジェクトとBeanをデータソースにどのようにマップするかを記述するのに、マッピング・エディタで生成されるメタデータを使用します。このアプローチにより、永続性情報とオブジェクト・モデルが分離されるため、開発者は扱いやすい独自のオブジェクト・モデルを、DBAは扱いやすい独自のスキーマをそれぞれ自由に設計できます。

3.4.1 マッピングのタイプ

ADF内のTopLinkでは、リレーショナル・マッピングとオブジェクト・リレーショナル(O/R)・マッピングがサポートされます。

  • リレーショナル・マッピング: 任意のオブジェクト・データ・メンバー・タイプを、サポートされるリレーショナル・データベース内で対応するリレーショナル・データベース(SQL)のデータソース表現に変換するマッピング。リレーショナル・マッピングにより、オブジェクト・モデルをリレーショナル・データ・モデルにマップできます。

  • オブジェクト・リレーショナル(O/R)・マッピング: 特定のオブジェクト・データ・メンバー・タイプを、Oracleデータベースなどの特別なオブジェクト・リレーショナル・データベースの記憶域にとって最適となる構造化されたデータソース表現に変換するマッピング。オブジェクト・リレーショナル・マッピングにより、オブジェクト・モデルをオブジェクト・リレーショナル・データ・モデルにマップできます。

3.4.2 ダイレクト・マッピング

TopLinkでは、次のダイレクト・マッピングを作成できます。

  • フィールドへ直接マッピング: Java属性をデータベース・フィールドに直接マップします。

  • タイプ変換マッピング: 文字と文字列のマッピングなど、単純なタイプ変換でJava値をマップします。

  • オブジェクト・タイプ・マッピング: アソシエーションを使用して値をデータベースにマップします。

  • シリアライズ・オブジェクト・マッピング: マルチメディア・オブジェクトなどのシリアライズ可能オブジェクトをデータベースのBLOBフィールドにマップします。

  • トランスフォーメーション・マッピング: 属性に格納されるオブジェクトを作成するために1つ以上のフィールドを使用できるカスタム・マッピングを作成できます。

3.4.3 ダイレクト・マッピングの作成方法

Javaクラスをデータベース表に直接マップするには、構造ウィンドウのTopLinkマップでJava属性を選択します。Oracle JDeveloperにより、選択した属性に使用可能なマッピングのリストが表示されます(図3-9を参照)。

図3-9 マッピング・エディタ

マッピング・エディタ

TopLinkの自動マップ機能を使用して、特定のJavaクラスまたはパッケージの属性を自動的にマップすることも可能です。詳細は、Oracle JDeveloperのオンライン・ヘルプを参照してください。

3.4.4 ダイレクト・マッピング作成時の処理

例3-4は、「フィールドへ直接」を使用してダイレクト・マッピングを作成したときに、Oracle JDeveloperによって生成されるJavaコードを示しています。この例では、Productsクラスのdescription属性が、データベース表のフィールドに直接マップされます。

例3-4 ダイレクト・マッピングのJavaコード

...
package oracle.srdemo.model;
public class Products {

  private String description;

    public String getDescription() {
      return this.description;
    }

    public void setDescription(String description) {
      this.description = description;
    }
}

3.4.5 留意事項

マッピング・エディタを使用すると、TopLinkマッピングをカスタマイズできます。ダイレクト・マッピングにおける一般的なカスタマイズは、次のとおりです。

  • マッピングを読取り専用として指定します。これらのマッピングは、更新操作や削除操作に含まれなくなります。

  • カスタムのgetおよびsetメソッドを使用します。

  • デフォルト値を定義します。データベースの実際のフィールドがNULLの場合、この値が使用されます。

図3-10は、マッピング・エディタの「フィールドへ直接」マッピングの「一般」タブを示しています。各ダイレクト・マッピング(3.4.2項「ダイレクト・マッピング」を参照)には、追加の特別オプションが含まれる場合もあります。詳細は、Oracle JDeveloperのオンライン・ヘルプを参照してください。

図3-10 「フィールドへ直接」マッピングの例

「フィールドへ直接」マッピングの例

3.5 関連を使用した関連クラスのマッピング

リレーショナル・マッピングでは、永続オブジェクトが他の永続オブジェクトを参照する方法を定義します。TopLinkでは、次の関連マッピングが提供されます。

リレーショナル・マッピングとオブジェクト・リレーショナル・マッピングを混同しないでください。オブジェクト・リレーショナル・マッピングでは、オブジェクト・モデルをOracleデータベースなどのオブジェクト・リレーショナル・データ・モデルにマップできます。TopLinkでは、次のマッピングを作成できます。

これらのマッピングは、Oracle TopLinkランタイムでサポートされますが、Javaコードで作成する必要があります。マッピング・エディタは使用できません。

3.5.1 関連マッピングの作成方法

ダイレクト・マッピング(3.4.3項「ダイレクト・マッピングの作成方法」を参照)の場合と同様に、Javaクラスをデータベース表に直接マップするには、構造ウィンドウのTopLinkマップでJava属性を選択します。

関連マッピングのマッピング・エディタには、データベース表の関連を定義(または作成)するための「表参照」タブが含まれます。

図3-11 「表参照」タブの例

「表参照」タブの図

詳細は、Oracle JDeveloperのオンライン・ヘルプを参照してください。

3.5.2 関連作成時の処理

例3-5は、「フィールドへ直接」を使用してダイレクト・マッピングを作成したときに、Oracle JDeveloperによって生成されるJavaコードを示しています。この例では、ServiceRequestクラスのaddress属性が、別のクラスであるUserに対して1対1の関連を保持しています(つまり、各ServiceRequestは、1つのUserにより作成されています)。

例3-5 関連マッピングのJavaコード

package oracle.srdemo.model;


    /**Map createdBy <-> oracle.srdemo.model.Users
     * @associates <{oracle.srdemo.model.Users}>
     */
  private ValueHolderInterface createdBy;

    public Users getCreatedBy() {
      return (Users)this.createdBy.getValue();
    }

    public void setCreatedBy(Users createdBy) {
      this.createdBy.setValue(createdBy);
    }

3.5.3 留意事項

マッピング・エディタを使用すると、TopLinkマッピングをカスタマイズできます。関連マッピングにおける一般的なカスタマイズは、次のとおりです。

  • マッピングを読取り専用として指定します。これらのマッピングは、更新操作や削除操作に含まれなくなります。

  • カスタムのgetおよびsetメソッドを使用します。

  • デフォルト値を定義します。データベースの実際のフィールドがNULLの場合、この値が使用されます。

  • インダイレクションを使用します。TopLinkでインダイレクションを使用すると、参照オブジェクトのプレースホルダとしてインダイレクション・オブジェクトが使用されます。TopLinkでは、特定の属性に対するアクセスが発生するまで依存オブジェクトの読取りを遅延します。

  • プライベートな関連または独立した関連を構成します。プライベートな関連では、ターゲット・オブジェクトがソース・オブジェクトのプライベート・コンポーネントとなります。ソース・オブジェクトを破棄すると、ターゲット・オブジェクトも破棄されます。独立した関連では、ソース・オブジェクトとターゲット・オブジェクトが相互に独立して存在します。一方のオブジェクトの破棄は、必ずしも他方の破棄につながりません。

  • 双方向関連を指定します。この場合、関連内の2つのクラスが、1対1マッピングを使用して相互に参照を行います。

図3-12は、マッピング・エディタの1対1マッピングの「一般」タブを示しています。「表参照」タブ(図3-13を参照)を使用すると、マッピングの外部キー参照を定義できます。各ダイレクト・マッピング(3.5項「関連を使用した関連クラスのマッピング」を参照)には、追加の特別オプションが含まれる場合もあります。詳細は、Oracle JDeveloperのオンライン・ヘルプを参照してください。

図3-12 1対1マッピングの「一般」タブの例

1対1マッピングの「一般」タブの例

図3-13 1対1マッピングの「表参照」タブの例

1対1マッピングの「表参照」タブの例

3.6 主キーによるオブジェクトの検索

TopLinkでは、Objectとして主キーを使用する事前定義済のファインダ(findByPrimaryKey)を提供しています。このファインダは、実行時に定義されます(マッピング・エディタでは定義されません)。

例3-6 主キー・ファインダの実行

{
    Employee employee = getEmployeeHome().findByPrimaryKey(primraryKey);
}

3.7 オブジェクトの問合せ

オブジェクトを問い合せるには、TopLink名前付き問合せを作成してから、問合せで指定したクラスのデータ・コントロールを作成します。これにより、TopLink問合せをデータ・コントロールに公開できます。

名前付き問合せは、後から取得および実行するために作成して保存するTopLink問合せです。名前付き問合せ(および関連するすべてのサポート・オブジェクト)は、一度準備しておくと後で効率的に再利用することが可能であり、頻繁に実行される操作に適しているため、その使用によりアプリケーションのパフォーマンスが向上します。

次の問合せを作成できます。

3.7.1 問合せの作成方法

TopLink式ビルダー、SQL式またはEJB QL式を使用して、TopLink名前付き問合せを作成できます。マッピング・エディタ(図3-14を参照)を使用すると、ディスクリプタ・レベルまたはセッション・レベルで問合せを構成できます。

図3-14 「名前付き問合せ」タブ

「名前付き問合せ」タブの例

3.7.2 留意事項

3.7.2.1 Query By Example(QBE)の使用

Query By Exampleでは、問合せに使用する属性のみを入力可能なサンプル・オブジェクト・インスタンスのフォームに問合せ選択基準を指定できます。Query By Exampleを定義するには、ReadObjectQueryまたはReadAllQueryに対して、サンプルの永続オブジェクト・インスタンスおよび(オプションで)Query By Exampleポリシーを指定します。

ADFでは、TopLinkのQuery By Exampleにより実行されるのは、メモリー内問合せのみです。

3.7.2.2 問合せ結果のソート

TopLink問合せのソート基準は、Oracle JDeveloperからは構成できません。ディスクリプタ修正メソッドを使用して、Javaメソッドを記述する必要があります。詳細は、3.3.3.2項「修正メソッドの使用」を参照してください。

3.8 作業ユニットでのオブジェクトの作成と変更

データベースのトランザクションは、単一の操作として成功するか失敗する複数の操作(作成、読取り、更新または削除)のセットです。失敗したトランザクションは、データベースにより破棄(ロールバック)されるため、その場合データベースは元の状態のままとなります。

TopLinkでは、トランザクションは作業ユニット・オブジェクトに格納されます。作業ユニットは、セッションから取得するか、そのAPIを使用して取得します。トランザクションは、直接制御するか、Java Transaction API(JTA)などのJava 2 Enterprise Edition(J2EE)アプリケーション・サーバー・トランザクション・コントローラを使用して制御します。

作業ユニットにより、トランザクション内の変更は、正常にデータベースにコミットされるまで他のスレッドから隔離されます。他のトランザクション・メカニズムとは異なり、作業ユニットでは、トランザクション内のオブジェクトに対する変更、変更の順序、および他のTopLinkキャッシュを無効化する可能性のある変更を自動的に管理できます。

作業ユニットは、これらの問題を管理するために、最小限の変更セットの計算、参照整合性規則への準拠とデッドロック回避を目的とするデータベース・コールの順序付け、および変更済オブジェクトの共有キャッシュへのマージを行います。クラスタ化された環境の場合、作業ユニットは、調整されたキャッシュ内で他のサーバーとの変更の同期も実行します。

3.8.1 作業ユニットの作成方法

例3-7は、クライアントのセッション・オブジェクトから作業ユニットを取得する方法を示しています。

例3-7 作業ユニットの取得

public UnitOfWork acquireUnitOfWork() {

  Server server = getServer();

  if (server.hasExternalTransactionController()) {
    return server.getActiveUnitOfWork();
  server.acquireUnitOfWork();

}

3.8.1.1 作業ユニットでのオブジェクトの作成

作業ユニットで新規オブジェクトを作成する場合、コミット時に作業ユニットがそのオブジェクトをデータベースに書き込むことができるように、registerObjectメソッドを使用します。

作業ユニットは、1対1マッピングと1対多マッピングの外部キー情報を使用して、コミット順序を計算します。コミット・トランザクション中に制約問題が発生した場合は、マッピング定義を確認してください。registerObjectメソッドを使用してオブジェクトを登録する場合の順序は、コミット順序には影響しません。

例3-8例3-9は、作業ユニットのregisterObjectメソッドにより戻されるクローンを使用して、(関連のない)単純なオブジェクトを作成および永続化する方法を示しています。

例3-8 オブジェクトの作成: 推奨される方法

UnitOfWork uow = session.acquireUnitOfWork();
    Pet pet = new Pet();
    Pet petClone = (Pet)uow.registerObject(pet);
    petClone.setId(100);
    petClone.setName("Fluffy");
    petClone.setType("Cat");
uow.commit();

例3-9は、もう1つの一般的な方法を示しています。

例3-9 オブジェクトの作成: もう1つの方法

UnitOfWork uow = session.acquireUnitOfWork();
    Pet pet = new Pet();
    pet.setId(100);
    pet.setName("Fluffy");
    pet.setType("Cat");
    uow.registerObject(pet);
uow.commit();

どちらの方法でも、次のSQLが生成されます。

INSERT INTO PET (ID, NAME, TYPE, PET_OWN_ID) VALUES (100, 'Fluffy', 'Cat', NULL)

例3-8の方法をお薦めします。この方法は、クローンの操作パターンを理解するのに効果的で、将来的なコード変更に対する柔軟性が最も高いためです。新規オブジェクトとクローンを組み合せて操作すると、両方を混同して予期しない結果が発生する可能性があります。

3.8.1.2 作業ユニットの典型的な使用方法

TopLinkでは、作業ユニットが次のように使用されます。

  1. クライアント・アプリケーションは、セッション・オブジェクトから作業ユニットを取得します。

  2. クライアント・アプリケーションは、変更しようとするキャッシュ・オブジェクトをTopLinkに問い合せて取得し、そのキャッシュ・オブジェクトを作業ユニットに登録します。

  3. 作業ユニットは、オブジェクトの変更ポリシーに従ってオブジェクトを登録します。

    デフォルトでは、オブジェクトが登録されるたびに、作業ユニットはセッション・キャッシュまたはデータベースのオブジェクトにアクセスし、バックアップ・クローンと作業クローンを作成します。作業ユニットは、クライアント・アプリケーションに作業クローンを戻します。

  4. クライアント・アプリケーションは、作業ユニットから戻された作業オブジェクトを変更します。

  5. クライアント・アプリケーション(または外部トランザクション・コントローラ)は、トランザクションをコミットします。

  6. 作業ユニットは、オブジェクトの変更ポリシーに従って、各登録済オブジェクトの変更セットを計算します。

    デフォルトでは、コミット時に、作業ユニットにより作業クローンとバックアップ・クローンが比較され、変更セットが計算されます(つまり、必要とされる最小限の変更が決定されます)。同じオブジェクトに対する同時変更が不正な変更と識別されないように、この比較はバックアップ・クローンを対象に実行されます。その後、作業ユニットにより、新規オブジェクトまたは変更オブジェクトのデータベースへのコミットが試行されます。

    コミット・トランザクションに成功すると、作業ユニットは、変更を共有セッション・キャッシュにマージします。コミットに失敗した場合、共有キャッシュのオブジェクトに対する変更は発生しません。変更がない場合、作業ユニットは新規トランザクションを開始しません。

図3-15 作業ユニットのライフサイクル

作業ユニットのライフサイクル

例3-10は、コードでのデフォルトのライフサイクルを示しています。

例3-10 作業ユニットのライフサイクル

// The application reads a set of objects from the database 
Vector employees = session.readAllObjects(Employee.class);

// The application specifies an employee to edit
. . .
Employee employee = (Employee) employees.elementAt(index);

try {
    // Acquire a unit of work from the session
    UnitOfWork uow = session.acquireUnitOfWork();

    // Register the object that is to be changed. Unit of work returns a clone
    // of the object and makes a backup copy of the original employee
    Employee employeeClone = (Employee)uow.registerObject(employee);

    // Make changes to the employee clone by adding a new phoneNumber. 
    // If a new object is referred to by a clone, it does not have to be
    // registered. Unit of work determines it is a new object at commit time
    PhoneNumber newPhoneNumber = new PhoneNumber("cell","212","765-9002");
    employeeClone.addPhoneNumber(newPhoneNumber);

    // Commit the transaction: unit of work compares the employeeClone with
    // the backup copy of the employee, begins a transaction, and updates the
    // database with the changes. If successful, the transaction is committed
    // and the changes in employeeClone are merged into employee. If there is an
    // error updating the database, the transaction is rolled back and the
    // changes are not merged into the original employee object
    uow.commit();
} catch (DatabaseException ex) {

    // If the commit fails, the database is not changed. The unit of work should
    // be thrown away and application-specific action taken
}
// After the commit, the unit of work is no longer valid. Do not use further

3.8.2 作業ユニット変更時の処理

例3-11では、作業ユニットの前にPetが読み取られます。変数petは、このPetのキャッシュ・コピー・クローンです。このコードでは、作業ユニット内でキャッシュ・コピーを登録して作業コピー・クローンを取得します。次に、作業コピー・クローンを変更して作業ユニットをコミットします。

例3-11 オブジェクトの変更

// Read in any pet
Pet pet = (Pet)session.readObject(Pet.class);
UnitOfWork uow = session.acquireUnitOfWork();
    Pet petClone = (Pet) uow.registerObject(pet);
    petClone.setName("Furry");
uow.commit();

例3-12では、作業ユニットを通じた問合せによりクローンを戻すことができるという事実を利用して登録手順を省略しています。ただし、この方法には、キャッシュ・コピー・クローンのハンドルを取得できないというデメリットがあります。

コミット・トランザクション後に、更新されたPetを操作する場合は、セッションに問い合せて取得する必要があります(作業ユニットがコミットされると、そのクローンは無効となるため、使用できないことに注意してください)。

例3-12 オブジェクトの変更: 登録手順の省略

UnitOfWork uow = session.acquireUnitOfWork();
    Pet petClone = (Pet) uow.readObject(Pet.class);
    petClone.setName("Furry");
uow.commit();

どちらの方法でも、次のSQLが生成されます。

UPDATE PET SET NAME = 'Furry' WHERE (ID = 100)

作業ユニットを通じた問合せは、慎重に使用してください。問合せで読み取られるすべてのオブジェクトは、作業ユニットに登録されるため、コミット時に変更をチェックされます。アプリケーション設計では、作業ユニットを通じてReadAllQueryを実行するより、セッションを通じてReadAllQueryを実行し、変更が必要なオブジェクトのみを作業ユニットに登録した方が、パフォーマンス的には有利です。

3.8.2.1 オブジェクトの削除

作業ユニットのオブジェクトを削除するには、deleteObjectまたはdeleteAllObjectsメソッドを使用します。まだ作業ユニットに登録されていないオブジェクトを削除すると、作業ユニットによってそのオブジェクトが自動的に登録されます。

オブジェクトを削除すると、TopLinkにより、そのオブジェクトがプライベートに所有する子の部分も削除されます。これらの部分は、所有する(親の)オブジェクトがないと存在できないためです。コミット時に、作業ユニットにより、データベース制約に従いながらオブジェクトを削除するSQLが生成されます。

3.8.2.1.1 データベースからのオブジェクトの明示的な削除

プライベートに所有された関連内でガベージ・コレクションの対象とならないオブジェクト(特にオブジェクト・モデルのルート・オブジェクト)が存在する場合、例3-13のようにdeleteObject APIを使用して、オブジェクトを表現している行を明示的に削除できます。

例3-13 明示的な削除

UnitOfWork uow = session.acquireUnitOfWork();
    pet petClone = (Pet)uow.readObject(Pet.class);
    uow.deleteObject(petClone);
uow.commit();

このコードにより、次のSQLが生成されます。

DELETE FROM PET WHERE (ID = 100)

3.8.3 留意事項

TopLinkの作業ユニットは、強力なトランザクション・モデルです。この項に記載した内容の追加情報は、『Oracle TopLink開発者ガイド』のTopLinkのトランザクションに関する章を参照してください。

3.8.3.1 作業ユニットと変更ポリシー

作業ユニットは、オブジェクトのディスクリプタに構成された変更ポリシーに基づいて、登録されたオブジェクトの変更を追跡します。変更がない場合、作業ユニットは新規トランザクションを開始しません。

表3-1に、TopLinkが提供する変更ポリシーを示します。

表3-1 TopLinkの変更ポリシー

変更ポリシー 適用先

遅延変更検出ポリシー

オブジェクトの変更特性の広い範囲

デフォルトの変更ポリシー

オブジェクト・レベルの変更追跡ポリシー

属性が少ないオブジェクト、または属性が多く、変更された属性が多いオブジェクト

属性変更追跡ポリシー

属性が多く、変更された属性が少ないオブジェクト

最も効率的な変更ポリシー

OC4JでのEJB 3.0または2.x CMPに対するデフォルトの変更ポリシー


3.8.3.2 ネストされた作業ユニットとパラレル作業ユニット

TopLinkを使用して次の作業ユニットを作成できます。

3.8.3.2.1 ネストされた作業ユニット

作業ユニット(子)は、別の作業ユニット(親)内でネストできます。ネストされた作業ユニットは、変更をデータベースにコミットしません。かわりに、変更は親の作業ユニットに渡され、コミット時に親によって変更のコミットが試行されます。作業ユニットをネストすることで、規模の大きなトランザクションを小さな個別のトランザクションに分割できます。この場合、次の処理が保証されます。

  • ネストされた各作業ユニットの変更は、1つのグループとしてコミットされるか、または失敗します。

  • ネストされた作業ユニットの失敗は、親の作業ユニットで発生する他の操作のコミットまたはロールバック・トランザクションに影響しません。

  • 変更は、単一のトランザクションとしてデータベースに提供されます。

3.8.3.2.2 パラレル作業ユニット

作業ユニットでは、オブジェクトのコピーが操作されるため、複数の作業ユニット・インスタンスで同じオブジェクトをパラレルに変更できます。TopLinkでは、作業ユニットによる変更のコミット時に並行処理の問題を解決します。

3.9 ストアド・プロシージャとの対話

式やSQL文字列のかわりに、StoredProcedureCallオブジェクトを問合せに指定できます。ただし、プロシージャでは、問合せ対象のクラスのインスタンスを作成するのに必要なすべてのデータが戻される必要があります。

例3-14 ストアド・プロシージャでのReadAll問合せ

ReadAllQuery readAllQuery = new ReadAllQuery();
call = new StoredProcedureCall();
call.setProcedureName("Read_All_Employees");
readAllQuery.setCall(call);
Vector employees = (Vector) session.executeQuery(readAllQuery);

StoredProcedureCallを使用して、次の操作を実行できます。


注意:

StoredProcedureCallOUTまたはINOUTパラメータと組み合せて使用する場合、DatabaseQuerybindAllParametersメソッドを使用する必要はなくなりました。ただし、すべてのOUTおよびINOUTパラメータに対して常にJava型を指定する必要があります。指定しない場合、デフォルトでString型が使用されることに注意してください。

3.9.1 入力パラメータの指定

例3-15では、StoredProcedureCalladdNamedArgumentメソッドを使用して、入力パラメータとしてPOSTAL_CODEパラメータを指定しています。引数の値は、addNamedArgumentValueメソッドを使用して指定できます。

例3-15 入力パラメータ付きのストアド・プロシージャ・コール

StoredProcedureCall call = new StoredProcedureCall();
call.setProcedureName("CHECK_VALID_POSTAL_CODE");
call.addNamedArgument("POSTAL_CODE");
call.addNamedArgumentValue("L5J1H5");
call.addNamedOutputArgument(
    "IS_VALID",    // procedure parameter name
    "IS_VALID",    // out argument field name
    Integer.class  // Java type corresponding to type returned by procedure
);
ValueReadQuery query = new ValueReadQuery();
query.setCall(call);
Number isValid = (Number) session.executeQuery(query);

引数を追加する順序は、引数の値を追加する順序と一致している必要があります。例3-16では、引数NAMEが値Julietに、引数SALARYが値80000にバインドされます。

例3-16 ストアド・プロシージャ・コールでの引数と値の一致

StoredProcedureCall call = new StoredProcedureCall();
call.setProcedureName("CHECK_VALID_POSTAL_CODE");
call.addNamedArgument("NAME");
call.addNamedArgument("SALARY");
call.addNamedArgumentValue("Juliet");
call.addNamedArgumentValue(80000);

3.9.2 出力パラメータの指定

出力パラメータにより、ストアド・プロシージャで追加の情報を戻すことが可能になります。オブジェクトの作成に必要とされるすべてのフィールドを戻す場合、出力パラメータを使用してreadObjectQueryを定義できます。

例3-17では、StoredProcedureCalladdNamedOutputArgumentメソッドを使用して、出力パラメータとしてIS_VALIDパラメータを指定しています。

例3-17 出力パラメータ付きのストアド・プロシージャ・コール

StoredProcedureCall call = new StoredProcedureCall();
call.setProcedureName("CHECK_VALID_POSTAL_CODE");
call.addNamedArgument("POSTAL_CODE");
call.addNamedOutputArgument(
    "IS_VALID",    // procedure parameter name
    "IS_VALID",    // out argument field name
    Integer.class  // Java type corresponding to type returned by procedure
);
ValueReadQuery query = new ValueReadQuery();
query.setCall(call);
query.addArgument("POSTAL_CODE");
Vector parameters = new Vector();
parameters.addElement("L5J1H5");
Number isValid = (Number) session.executeQuery(query,parameters);

注意:

データを戻す出力パラメータの使用に対応していないデータベースもあります。ただし、そのようなデータベースでは、一般的にストアド・プロシージャから結果セットを戻すことができるため、出力パラメータは必要ありません。

Oracleデータベースを使用している場合、TopLinkのカーソル問合せ結果およびストリーム問合せ結果を利用できます。

3.9.3 入出力パラメータの指定

例3-18では、StoredProcedureCalladdNamedInOutputArgumentValueメソッドを使用して、入出力パラメータとしてLENGTHパラメータを指定し、ストアド・プロシージャに渡す引数の値を指定しています。引数の値を指定しない場合は、addNamedInOutputArgumentメソッドを使用します。

例3-18 入出力パラメータ付きのストアド・プロシージャ・コール

StoredProcedureCall call = new StoredProcedureCall();
call.setProcedureName("CONVERT_FEET_TO_METERs");
call.addNamedInOutputArgumentValue(
    "LENGTH",          // procedure parameter name
    new Integer(100),  // in argument value
    "LENGTH",          // out argument field name
    Integer.class      // Java type corresponding to type returned by procedure
)
ValueReadQuery query = new ValueReadQuery();
query.setCall(call);
Integer metricLength = (Integer) session.executeQuery(query);

3.9.4 出力パラメータ・イベントの使用

TopLinkでは、データベースでサポートされる出力パラメータ・イベントを管理できます。たとえば、アプリケーションでエラー状態をチェックすることが可能なエラー・コードがストアド・プロシージャから戻される場合、TopLinkでセッション・イベントのOutputParametersDetectedを起動し、アプリケーションでそれらの出力パラメータを処理できます。

例3-19 リセット設定と出力パラメータ・エラー・コード付きのストアド・プロシージャ

StoredProcedureCall call = new StoredProcedureCall();
call.setProcedureName("READ_EMPLOYEE");
call.addNamedArgument("EMP_ID");
call.addNamedOutputArgument(
    "ERROR_CODE",    // procedure parameter name
    "ERROR_CODE",    // out argument field name
    Integer.class  // Java type corresponding to type returned by procedure
);
ReadObjectQuery query = new ReadObjectQuery();
query.setCall(call);
query.addArgument("EMP_ID");
ErrorCodeListener listener = new ErrorCodeListener();
session.getEventManager().addListener(listener);
Vector args = new Vector();
args.addElement(new Integer(44));
Employee employee = (Employee) session.executeQuery(query, args);

3.9.5 StoredFunctionCallの使用

StoredProcedureCallを使用すると、ストアド・プロシージャをサポートするデータベースに定義されたストアド・プロシージャを起動できます。また、StoredFunctionCallを使用すると、ストアド・ファンクションをサポートするデータベース(DatabasePlatformsupportsStoredFunctionsメソッドでtrueが戻されるデータベース)に定義されたストアド・ファンクションを起動できます。

通常は、ストアド・プロシージャとストアド・ファンクションの両方で、入力パラメータ、出力パラメータおよび入出力パラメータを指定できます。ただし、ストアド・プロシージャは値を戻す必要がありませんが、ストアド・ファンクションは常に単一の値を戻します。

StoredFunctionCallクラスは、setResultという新規メソッドを追加することでStoredProcedureCallを拡張しています。このメソッドを使用して、TopLinkでストアド・ファンクションの戻り値を格納する場所の名前(または名前と型の両方)を指定します。

TopLinkでStoredFunctionCallの準備が行われる場合、そのSQLが検証され、次の場合にはValidationExceptionがスローされます。

  • 現在のプラットフォームでストアド・ファンクションがサポートされない場合

  • ユーザーが戻り型の指定に失敗した場合

例3-20では、StoredFunctionCallsetProcedureNameメソッドを使用してストアド・ファンクションの名前を設定していることに注意してください。

例3-20 StoredFunctionCallの作成

StoredFunctionCall functionCall = new StoredFunctionCall();
functionCall.setProcedureName("READ_EMPLOYEE");
functionCall.addNamedArgument("EMP_ID");
functionCall.setResult("FUNCTION_RESULT", String);
ReadObjectQuery query = new ReadObjectQuery();
query.setCall(functionCall);
query.addArgument("EMP_ID");
Vector args = new Vector();
args.addElement(new Integer(44));
Employee employee = (Employee) session.executeQuery(query, args);

3.9.6 問合せによる順序付け

問合せによる順序付けでは、カスタムの読取り問合せ(ValueReadQuery)、更新問合せ(DataModifyQuery)、および指定した事前割当てサイズを使用して、順序リソースにアクセスできます。これにより、ストアド・プロシージャを使用して順序付けを実行し、TopLinkに含まれる他の順序付けタイプでサポートされない順序リソースにアクセスできます。

3.10 ADFデータ・コントロールによるサービスの公開

ユーザー・インタフェースにサービスをバインドする最も簡単な方法は、ADFデータ・コントロールを使用することです。

この項には、次の内容に関する情報が含まれます。

3.10.1 ADFデータ・コントロールの作成方法

EJBセッションBeanからADFデータ・コントロールを作成するには、ナビゲータでセッションBeanを右クリックして「データ・コントロールの作成」を選択するか、セッションBeanをデータ・コントロール・パレットにドラッグします。


注意:

Oracle固有のライブラリに依存しないJ2EE開発者は、ADFデータ・コントロールのかわりにマネージドBeanを使用できます。この方法は、複雑であり、このマニュアルの対象範囲外です。

EJB 3.0セッションBeanからデータ・コントロールを作成すると、いくつかのXMLファイルが生成され、ナビゲータに表示されます。次からの各項で、生成されるファイルとデータ・コントロール・パレットについて説明します。

3.10.2 データ・コントロール・ファイルの理解

データ・コントロールを作成すると、次のXMLファイルがモデルに生成されます。

  • DataControls.dcx: データ・コントロール定義ファイル

  • <session_bean>.xml: 構造定義ファイル

  • ReadOnlyCollection.xml: 設計時XMLファイル

  • ReadOnlySingleValue.xml: 設計時XMLファイル

  • UpdateableCollection.xml: 設計時XMLファイル

  • UpdateableSingleValue.xml: 設計時XMLファイル

  • <entity_name>.xml: エンティティ定義ファイル(エンティティごとに1ファイル)

これらのファイルの相互関係と使用方法の詳細は、付録A「ADF XMLファイルのリファレンス」を参照してください。

3.10.2.1 DataControls.dcxファイルについて

DataControls.dcxファイルは、ビジネス・サービスのデータ・コントロールを登録すると作成されます。.dcxファイルは、クライアントと使用可能なビジネス・サービス間の対話を支援するOracle ADF Modelレイヤーのアダプタ・クラスを識別します。EJB、WebサービスおよびBeanベースのデータ・コントロールの場合、プロパティ・インスペクタでこのファイルを編集し、パラメータの追加と削除、およびデータ・コントロール設定の変更を行えます。たとえば、.dcxファイルを使用して様々な項目のグローバル・プロパティを設定できます(ソートの有効化と無効化など)。

3.10.2.2 構造定義ファイルについて

セッションBeanをOracle ADFデータ・コントロールとして登録すると、すべてのセッションBeanに対応するXML定義ファイルがModelプロジェクトに作成されます。このファイルは、一般的に構造定義ファイルと呼ばれます。構造定義ファイルには、セッションBeanと同じ名前が付けられますが、拡張子は.xmlです。

構造定義は、次の3つのタイプのオブジェクトで構成されます。

  • 属性

  • アクセッサ

  • 操作

3.10.2.3 エンティティXMLファイルについて

データ・コントロールを作成すると、エンティティ(TopLink、EJBまたはJava Bean)ごとに1つのXMLファイルが作成されます。これらのファイルは、ADFの設計時と実行時の両方に使用されます。これらのファイルには、クラスの構造と、各属性のUIヒント、バリデータおよびラベルが記述されています。

3.10.2.4 設計時XMLファイルについて

設計時専用に次の4つのファイルが生成されます。

  • ReadOnlyCollection.xml

  • ReadOnlySingleValue.xml

  • UpdateableCollection.xml

  • UpdateableSingleValue.xml

これらのファイルは、使用可能な操作を記述したCollectionBeanClassとしてMethodAccessor定義によって参照されます。通常、このファイルを手動で編集することはありませんが、データ・コントロール・パレットで項目をカスタマイズできます。

3.10.3 データ・コントロール・パレットの理解

クライアント開発者は、データ・コントロール・パレットを使用して、データ・バインドされたHTML要素(JSPページ用)、データ・バインドされたFaces要素(JSF JSPページ用)、およびデータ・バインドされたSwing UIコンポーネント(ADF Swingパネル用)を作成できます。データ・コントロール・パレットには、次の2つの選択リストがあります。

  • 使用可能なビジネス・オブジェクト、メソッド、およびデータ・コントロール操作を階層状に表示したリスト

  • 指定したビジネス・オブジェクト用に選択して表示中のクライアント文書にドロップできる、適切なビジュアル要素のドロップダウン・リスト

また、Webアプリケーション開発者は、データ・コントロール・パレットを使用してビジネス・サービスの提供するメソッドを選択し、それらをページ・フローのデータ・ページやデータ・アクション上にドロップできます。

パレットは前述の各項で説明したXMLファイルを直接反映しているため、ファイルを編集するとパレット内の要素も変更されます。

データ・コントロール・パレットに表示されるビジネス・サービスの階層構造は、Modelプロジェクトのデータ・コントロールに登録されているビジネス・サービスによって決定されます。パレットには、登録されたビジネス・サービスごとに個別のルート・ノードが表示されます。

図3-16 データ・コントロール・パレット

データ・コントロール・パレットの図

3.10.3.1 データ・コントロール・ビジネス・オブジェクトの概要

データ・コントロール・パレットのルート・ノードは、ビジネス・サービス用に登録されたデータ・コントロールを表します。データ・コントロールのルート・ノードの下には、Beanベースのビジネス・サービスが、コンストラクタ、属性、アクセッサまたは操作として表示されます。

  • コンストラクタ: 作成可能な型は、「コンストラクタ」ノード内に格納されます。これらの型は、オブジェクトのデフォルト・コンストラクタをコールします。

  • 属性: Beanプロパティなど。これらのプロパティで、単純なスカラー値オブジェクト、構造化オブジェクト(Bean)またはコレクションを定義できます。

  • アクセッサ: get()メソッドとset()メソッド。

  • 操作: Beanメソッドなど。これらのメソッドは、値を戻す場合と戻さない場合があり、メソッド・パラメータも使用する場合と使用しない場合があります。Webサービスの場合、データ・コントロール・パレットには操作のみが表示されます。

データ・コントロール・パレットの使用方法の詳細は、第5章「ページでのデータの表示」を参照してください。データ・コントロール・ファイルとその相互関係の詳細は、付録A「ADF XMLファイルのリファレンス」を参照してください。

3.10.3.2 ビジネス・サービス変更後のADFデータ・コントロールのリフレッシュ

Modelプロジェクトのデータ・コントロール定義をすでに作成済の場合、ビジネス・サービスの変更後にデータ・コントロールを更新できます。データ・コントロール定義をリフレッシュすると、ADFアプリケーションでビジネス・サービスの最新の変更を使用できます。データ・コントロール定義をリフレッシュするための操作は、Modelプロジェクトに対する変更のタイプに応じて異なります。

3.10.3.2.1 データ・コントロール・パレットでの変更済データ・コントロールの表示

パレットが表示されていない場合は、「表示」メニューの「データ・コントロール・パレット」を選択します。パレットが表示されている場合は、パレット内で右クリックして「リフレッシュ」を選択します。

3.10.3.2.2 変更されたビジネス・サービスのデータ・コントロール定義のリフレッシュ

Modelプロジェクトで、作成するBeanまたは他のビジネス・サービスの新規プロパティを定義します。.javaファイルをコンパイルすると、対応する.xmlファイルにビジネス・サービスのメタデータが再生成されます。変更されたビジネス・サービスがBeanベース(EJBセッションBeanなど)の場合は、Beanの.xmlファイルを右クリックして「リフレッシュ」を選択します。

注意: ADF Business Componentsの場合、データ・コントロール定義は、ADF BCのプロジェクト・ファイルが変更されるたびに自動的に更新されます。

3.10.3.2.3 削除されたビジネス・サービスのデータ・コントロール定義の削除

データ・コントロール定義を削除するには、ビュー・プロジェクトでDataBindings.dcxファイルを選択し、構造ウィンドウでModelプロジェクトに表示されなくなったビジネス・サービスを表すデータ・コントロール・ノードを選択します。そのデータ・コントロール・ノードを右クリックし、「削除」を選択します。

JDeveloperにより、Modelプロジェクトのデータ・コントロール定義ファイル(DataBindings.dcx)が更新されます。DataBindings.dcxファイルは、クライアントと使用可能なビジネス・サービス間の対話を支援するOracle ADF Modelレイヤーのアダプタ・クラスを識別します。

3.10.3.2.4 ビジネス・サービスの名前変更または移動後のデータ・コントロールの更新

Modelプロジェクトで、ビジネス・サービスの名前を変更した場合やビジネス・サービスを新規パッケージに移動した場合、クライアントのデータ・コントロール定義内でModelプロジェクトに対する参照を更新する必要があります。

ビュー・プロジェクトで、DataBindings.dcxファイルを選択します。構造ウィンドウで、移動されたビジネス・サービスを表すデータ・コントロール・ノードを選択します。プロパティ・インスペクタで、パッケージ属性を編集して新規パッケージ名を指定します。