BMP では、さまざまなパーシスタンス機能ポイント (create、load、store など) でコンテナから EntityBean インターフェイスの所定のメソッドが呼び出されます。クラス実装には、次のメソッドによるパーシスタンス ロジックが含まれていることが必要です。
ejbCreate および ejbPostCreate
ejbLoadejbStoreejbRemove
さらに、エンティティ Bean によって、ejbFindByPrimaryKey メソッドがサポートされている必要があります。
後続のセクションでは、BMP を利用する際のクラス実装内の関連コードについて詳しく説明します。
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 実装では、データベースに接続し、データを取得し、_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);
}
}
|
メモ
|
コンテナでは、ビジネス メソッドの終了直後に 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 の呼び出しを制御します。
InstanceManager.setDirty(true) メソッドを呼び出します。
InstanceManager.setDirty(false) メソッドを呼び出します。ejbStore の呼び出しを強制するには、Bean の公開記述子の env-entry 要素で、ejipt.isAlwaysDirty プロパティを true に設定します。
データ ストアからエンティティ オブジェクトを削除するには、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());
}
}
ほとんどのクライアント アプリケーションでは、1 つまたは複数の行を取得し、場合によっては更新することによってデータベースと対話します。行を作成することはめったにありません。エンティティ Bean には、クライアントが 1 行以上の既存のデータにアクセスできる finder メソッドが実装されています。エンティティ Bean のホーム インターフェイスとその Bean 実装で、finder メソッドをコーディングします。
findByXxx メソッドを指定します。このメソッドは、Bean のリモート インターフェイスへの参照を返すか、あるいはリモート インターフェイス参照の Enumeration または Collection を返します。
ejbFindByXxx メソッドがあります。Bean 実装の finder メソッドではプライマリ キーを返すか、プライマリ キーの Enumeration または Collection を返します。
すべてのエンティティ Bean には、findByPrimaryKey と ejbFindByPrimaryKey メソッドのペアが必要です。このメソッドでは引数としてプライマリ キーを受け入れます。関連付けられている行が、プライマリ キーによって返されるデータ ストアに存在することを確認するか、または FinderException を返します。
次のコード例は、サンプル 2a の BalanceHome の findByPrimaryKey メソッドのホーム インターフェイスを示します。
...
Balance findByPrimaryKey(Integer key) throws FinderException, RemoteException; ...
次のコード例は、サンプル 2a の BalanceBean の ejbFndByPrimaryKey メソッドの 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;
}
...
アプリケーションの必要条件によって、エンティティ Bean に 1 つまたは複数の複数行 finder メソッドを実装するように選択できます。これらのメソッドはオプションであり、引数を取らなかったり、複数の引数を取ったりします。複数行 finder の Bean 実装では、認証を実行してオプションで FinderException を返し、プライマリ キーの Enumeration または Collection を返すか、あるいは Collection を実装するいずれかのクラスの子孫を返します。
次のコード例は、サンプル 7b の BankHome の findAllBanks メソッドのホーム インターフェイスを示します。
...
Collection findAllBanks() throws RemoteException; ...
次のコード例は、サンプル 7b の BankBean の ejbFindAllBanks メソッドの 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;
}
...