BMP では、さまざまなパーシスタンス機能ポイント (create、load、store など) でコンテナから EntityBean
インターフェイスの所定のメソッドが呼び出されます。クラス実装には、次のメソッドによるパーシスタンス ロジックが含まれていることが必要です。
ejbCreate
および ejbPostCreate
ejbLoad
ejbStore
ejbRemove
さらに、エンティティ 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 メソッドをコーディングします。
findBy
Xxx メソッドを指定します。このメソッドは、Bean のリモート インターフェイスへの参照を返すか、あるいはリモート インターフェイス参照の Enumeration
または Collection
を返します。
ejbFindBy
Xxx メソッドがあります。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; } ...