Bean の必須メソッド

BMP では、さまざまなパーシスタンス機能ポイント (create、load、store など) でコンテナから EntityBean インターフェイスの所定のメソッドが呼び出されます。クラス実装には、次のメソッドによるパーシスタンス ロジックが含まれていることが必要です。

さらに、エンティティ Bean によって、ejbFindByPrimaryKey メソッドがサポートされている必要があります。

後続のセクションでは、BMP を利用する際のクラス実装内の関連コードについて詳しく説明します。

ejbCreate および ejbPostCreate

ejbCreate メソッドは、オブジェクトを 1) 作成できるかどうか 2) 作成する必要があるかどうかを判別するために使用します。次の ejbCreate の例は、EJB サンプル 2a の BalanceBean からのものです。

...
public Integer ejbCreate(int accountId) throws CreateException {
  _value = 0;                
  Connection connection = null;

  try {
    Context context = new InitialContext();
    DataSource source =  
      (DataSource)context.lookup("java:comp/env/jdbc/source1");
    connection = source.getConnection();

    try {
      // 最初に ID が存在するかどうかをチェックします。
      // 存在する場合は DuplicateKeyException を返します。
      Statement statement = connection.createStatement();
      ResultSet rs = 
        statement.executeQuery("SELECT id FROM account WHERE id = " +
          accountId);
      if (rs.next())
        throw new DuplicateKeyException();
      statement.close();

      // ID が存在しない場合は、データを挿入します。
      statement =
        connection.createStatement();
      statement.executeUpdate
        ("INSERT INTO account (id, value) VALUES (" + accountId + ", " +
          _value +")");
      statement.close();
  }
  finally {
    connection.close();
  }
}
catch (NamingException naming) {
  throw new EJBException(naming);
}
catch (SQLException sql) {
  throw new EJBException(sql);
}

return new Integer(accountId);
} 
...

ejbPostCreate メソッドを使用して、いずれかのビジネス メソッドを呼び出す前に、Bean インスタンスに必要な追加の初期化を実行します。ejbCreate メソッドの引数リストと ejbPostCreate メソッドの引数リストは一致している必要があります。

ejbLoad

コンテナでは、ビジネス メソッドを呼び出す直前に ejbLoad を呼び出します。データ ストアからエンティティを取得し、フィールドをインスタンス変数にコピーする ejbLoad メソッドにコードを追加します。次の ejbLoad 実装では、データベースに接続し、データを取得し、_value インスタンス変数を設定します。

public void ejbLoad() {
  try {
    Context ctx = new InitialContext();
    DataSource ds = 
      (DataSource)ctx.lookup("java:comp/env/jdbc/source1");
    Connection connection = ds.getConnection();
    try {
      Statement statement = connection.createStatement();
      ResultSet results = 
        statement.executeQuery("SELECT value FROM account WHERE id = "
          + _context.getPrimaryKey());
      results.next();
      _value = results.getInt(1);

      results.close();
      statement.close();
    }
    finally {
      connection.close();
    }
  }
  catch (Exception e) {
    throw new EJBException(e);
  }
}

メモ

env-entry 要素または Bean プロパティで ejipt.isDataSharedfalse に設定した 場合、最初の ejbLoad または ejbCreate を使用してインスタンスがすでにロードされ ていると、コンテナでは ejbLoad を呼び出しません。ejipt.isDataCachedtrue に 設定した場合は、ロールバックの後のビジネス メソッドの前に ejbLoad を呼び出しま せん。また、アクティブ化の後のビジネス メソッドの前にも ejbLoad を呼び出しま せん。既定では、ロールバックまたはアクティブ化の後に再ロードします。


ejbStore

コンテナでは、ビジネス メソッドの終了直後に ejbStore を呼び出します。データ ストアにエンティティを保存する ejbStore メソッドにコードを追加してください。

次の ejbStore 実装では、まずデータベースに接続し、次にオブジェクトのステートによってデータベースを更新しています。

public void ejbStore() {0
  try {
    Context ctx = new InitialContext();
    DataSource ds = 
      (DataSource)ctx.lookup("java:comp/env/jdbc/source1");
    Connection connection = ds.getConnection();
    try {
      Statement statement = connection.createStatement();
      statement.executeUpdate("UPDATE account SET value = " + _value +
        " WHERE id = " + _context.getPrimaryKey());
       statement.close();
    }
    finally {
      connection.close();
    }
  }
  catch (Exception e) {
    throw new EJBException(e);
  }
}

EJB エンジンでは、ステート変化をチェックするときに、Bean インスタンスのメンバについて表面的な一致の比較 (つまり ==) しか行いません。このため、メンバ配列の要素だけが変更されていて、配列自体は変更されていない場合、EJB エンジンではステート変化は検出されません ("インスタンスのステートの変化"を参照)。このような最適化が必要なのは、深いデータ構造などで長時間の反復が発生した場合のパフォーマンス低下を避けるためです。

JRun では次のメソッドとプロパティを追加して、ejbStore の呼び出しを制御します。

ejbRemove

データ ストアからエンティティ オブジェクトを削除するには、ejbRemove メソッドを呼び出します。次の ejbRemove 実装では、まずデータベースに接続し、次にデータベース内のオブジェクトの削除を試みています。

public void ejbRemove() throws RemoveException {
  try {
    Context ctx = new InitialContext();
    DataSource ds = 
      (DataSource)ctx.lookup("java:comp/env/jdbc/source1");
    Connection connection = ds.getConnection();
    try {
      Statement statement = connection.createStatement();
      ResultSet results = 
        statement.executeQuery("DELETE FROM account WHERE id = " +
          _context.getPrimaryKey());
    }
    finally {
      connection.close();
    }
  }
  catch (Exception e) {
    throw new RemoveException(e.toString());
  }
}

finder メソッド

ほとんどのクライアント アプリケーションでは、1 つまたは複数の行を取得し、場合によっては更新することによってデータベースと対話します。行を作成することはめったにありません。エンティティ Bean には、クライアントが 1 行以上の既存のデータにアクセスできる finder メソッドが実装されています。エンティティ Bean のホーム インターフェイスとその Bean 実装で、finder メソッドをコーディングします。

1 行の finder メソッド

すべてのエンティティ Bean には、findByPrimaryKeyejbFindByPrimaryKey メソッドのペアが必要です。このメソッドでは引数としてプライマリ キーを受け入れます。関連付けられている行が、プライマリ キーによって返されるデータ ストアに存在することを確認するか、または FinderException を返します。

次のコード例は、サンプル 2a の BalanceHomefindByPrimaryKey メソッドのホーム インターフェイスを示します。

...
Balance findByPrimaryKey(Integer key) throws FinderException, 
RemoteException;
...

次のコード例は、サンプル 2a の BalanceBeanejbFndByPrimaryKey メソッドの Bean 実装を示します。

...
public Integer ejbFindByPrimaryKey(Integer key)
        throws FinderException {
  boolean exists = false;
  try {
    Context ctx = new InitialContext();
    DataSource ds = 
      (DataSource)ctx.lookup("java:comp/env/jdbc/source1");
    Connection connection = ds.getConnection();
    try {
      Statement statement = connection.createStatement();
      ResultSet results = 
        statement.executeQuery("SELECT value FROM account WHERE id = "
          + key);

      if (results.next()) exists = true;
    }
    finally {
      connection.close();
    }
  }
  catch (Exception e) {
    throw new EJBException(e);
  }
  // 行が見つからない場合は例外を返します。
  if (! exists) throw new FinderException();

  // 行が存在していることを示すプライマリ キーを返します。
  return key;
}
...

複数行 finder メソッド

アプリケーションの必要条件によって、エンティティ Bean に 1 つまたは複数の複数行 finder メソッドを実装するように選択できます。これらのメソッドはオプションであり、引数を取らなかったり、複数の引数を取ったりします。複数行 finder の Bean 実装では、認証を実行してオプションで FinderException を返し、プライマリ キーの Enumeration または Collection を返すか、あるいは Collection を実装するいずれかのクラスの子孫を返します。

次のコード例は、サンプル 7b の BankHomefindAllBanks メソッドのホーム インターフェイスを示します。

...
Collection findAllBanks() throws RemoteException;
...

次のコード例は、サンプル 7b の BankBeanejbFindAllBanks メソッドの Bean 実装を示します。

...
public Collection ejbFindAllBanks() {
  // 返す変数を定義します。ArrayList は AbstractCollection の子孫であり、
  // Collection を実装していることに注意してください。
  ArrayList names = new ArrayList();

  try {
    Context ctx = new InitialContext();
    DataSource ds = 
      (DataSource)ctx.lookup("java:comp/env/jdbc/source1");
    Connection connection = ds.getConnection();
    try {
      Statement stmt = connection.createStatement();
      ResultSet results = stmt.executeQuery("SELECT name FROM bank");

      while (results.next()) {
        names.add(results.getString(1));
      }
      results.close();
      stmt.close();
    }
    finally {
      connection.close();
    }
  }
  catch (NamingException naming) {
    throw new EJBException(naming);
  } 
  catch (SQLException sql) {
    throw new EJBException(sql);
  }

  // Collection (この場合は ArrayList) を返します。
  return names;
}
...