BMP エンティティ bean

BMP エンティティ bean では、フィールドやパーシスタンスロジックの getter メソッドや setter メソッドをコーディングします。

bean 実装で検出されたコールバックメソッドのパーシスタンスロジックを指定します。パーシスタンスロジックは、次に示すように、データを作成、取り出し、更新、保存または削除します。

EJB コンテナは、bean ライフサイクルのさまざまなポイントでこれらのメソッドを呼び出すことにより、データソースのパーシスタンスや一貫性を調整します。

メモ:  EJB コンテナは、CMP bean にこれらのメソッドを自動的に実装します。詳細については、 「CMP エンティティ bean (1.1 仕様)」 および 「CMP エンティティ bean (2.0 仕様)」 を参照してください。

ホームインターフェイス

BMP エンティティ bean のホームインターフェイスは、1 つ以上の create メソッド、1 つの findByPrimary キーメソッド、およびオプションでカスタム finder メソッドを定義します。

次のコードは、BMP エンティティ bean のリモートホームインターフェイス、および各メソッドから返される例外を示しています。

package compass;
import java.rmi.RemoteException;
import javax.ejb.CreateException;
import javax.ejb.FinderException;

public interface OrderHomeRemote extends javax.ejb.EJBHome {
  public OrderRemote create(String customerId, int tripId, String 
ccType, String ccNumber,
    String ccExpiration) throws CreateException, RemoteException;
  public OrderRemote findByPrimaryKey(Integer pk) throws 
FinderException, RemoteException;
  public java.util.Collection findByCustomer(String customerId) 
throws FinderException, RemoteException;
}

コンポーネントインターフェイス

BMP エンティティ bean のコンポーネントインターフェイスは、クライアントから呼び出されるビジネスメソッドを定義します。リモートコンポーネントインターフェイスで定義されるメソッドは、常に RemoteException を投げる必要があり、また、すべてのコンポーネントインターフェイスのメソッドは、bean 実装内のビジネスメソッドから投げられた例外を投げる必要があります。

次のコードは、BMP エンティティ bean のコンポーネントインターフェイスを示しています。

package compass;
import java.rmi.RemoteException;

public interface OrderRemote extends javax.ejb.EJBObject {
  public int getOrderId() throws RemoteException;
  public String getUserId() throws RemoteException;
  public void setUserId(String customerId) throws RemoteException;
  public int getTripId() throws RemoteException;
  public void setTripId(int tripId) throws RemoteException;
  public java.sql.Date getOrderDate() throws RemoteException;
  public String getCCType() throws RemoteException;
  public void setCCType(String ccType) throws RemoteException;
  public String getCCNumber() throws java.rmi.RemoteException;
  public void setCCNumber(String ccNumber) throws RemoteException;
  public String getCCExpiration() throws RemoteException;
  public void setCCExpiration(String ccType) throws RemoteException;
}

bean 実装

次のコードは、BMP エンティティ bean の bean 実装を示しています。

package compass;
import java.sql.*;
import javax.ejb.EJBException;
import javax.ejb.CreateException;
import javax.ejb.FinderException;
import javax.ejb.RemoveException;

public class OrderBean implements javax.ejb.EntityBean {

  public javax.ejb.EntityContext context;
  // これらのフィールドは、データベーステーブルの列に対応します。
  public int orderId;
  public String userId;
  public int tripId;
  public String ccType;
  public String ccNumber;
  public String ccExpiration;
  public java.sql.Date orderDate;

  public int getOrderId() {
    // orderID はプライマリキーです。
    return ((Integer) context.getPrimaryKey()).intValue();
  }
  public String getUserId(){
    return userId;
  }
  public void setUserId(String userId){
    this.userId = userId;
  }
  public int getTripId(){
    return tripId;
  }
  public void setTripId(int tripId){
    this.tripId = tripId;
  }
  public java.sql.Date getOrderDate(){
    return orderDate;
  }
  public String getCCType(){
    return ccType;
  }
  public void setCCType(String ccType){
    this.ccType = ccType;
  }
  public String getCCNumber(){
    return ccNumber;
  }
  public void setCCNumber(String ccNumber){
    this.ccNumber = ccNumber;
  }
  public String getCCExpiration(){
    return ccExpiration;
  }
  public void setCCExpiration(String ccExpiration){
    this.ccExpiration = ccExpiration;
  }
  // データベースに行を作成します。
  public java.lang.Integer ejbCreate(String userId, int tripId, String 
ccType, String ccNumber, String ccExpiration) throws 
CreateException {

    // log() はこのクラスの最後で定義されます。
    log("ejbCreate()");

    this.userId = userId;
    this.tripId = tripId;
    this.ccType = ccType;
    this.ccNumber = ccNumber;
    this.ccExpiration = ccExpiration;

    Connection connection=null;
    PreparedStatement ps=null;
    Statement stmt=null;
    ResultSet rs=null;

    try {
      javax.naming.InitialContext ctx = new 
javax.naming.InitialContext();
      javax.sql.DataSource ds = (javax.sql.DataSource) 
ctx.lookup("compass");
      connection=ds.getConnection();

      // 新規プライマリキーを自動生成します。
      stmt = connection.createStatement();
      stmt.executeUpdate("UPDATE sequence SET lastkey=lastkey+1 WHERE 
sequence_id='order'");
      rs = stmt.executeQuery("SELECT lastkey FROM sequence WHERE s
equence_id='order'");
      rs.next();
      orderId=rs.getInt(1);
      log("order:"+orderId);

      // 予約内容を挿入します。
      ps = connection.prepareStatement("INSERT INTO orders (order_id, 
user_id, trip_id, cc_type, cc_number, cc_expiration) VALUES 
(?,?,?,?,?,?)");
      ps.setInt(1, orderId);
      ps.setString(2, userId);
      ps.setInt(3, tripId);
      ps.setString(4, ccType);
      ps.setString(5, ccNumber);
      ps.setString(6, ccExpiration);

      if (ps.executeUpdate() == 1) {
        log("INSERT order succeeded");
      } else {
        log("INSERT order failed");
        throw new CreateException("INSERT order failed");
      }
      return new Integer(orderId);
    } catch (Exception e) {
      log(e);
      throw new EJBException(e.getMessage());
    } finally {
        try {
          rs.close();
          ps.close();
          connection.close();
        } catch (SQLException e) {
          log(e);
        }
    }
  }

  public void ejbPostCreate(String userId, int tripId, String ccType, 
String ccNumber, String ccExpiration) {
    // 実装されていません。
  }

  // データベースから行を取り出します。
  public void ejbLoad(){
    log("ejbLoad()");

    orderId = ((Integer) context.getPrimaryKey()).intValue();
    log("Primary key:"+orderId);

    Connection connection = null;
    PreparedStatement ps=null;
    ResultSet rs=null;

    try {
      javax.naming.InitialContext ctx = new 
javax.naming.InitialContext();
      javax.sql.DataSource ds = (javax.sql.DataSource) 
ctx.lookup("compass");
      connection=ds.getConnection();

      ps = connection.prepareStatement("SELECT * FROM orders WHERE 
order_id=?");
      ps.setInt(1, orderId);

      rs = ps.executeQuery();

      if (rs.next()) {
        log("SELECT order succeeded");
        userId = rs.getString("user_id");
        tripId = rs.getInt("trip_id");
        orderDate = rs.getDate("order_date");
        ccType = rs.getString("cc_type");
        ccNumber = rs.getString("cc_number");
        ccExpiration = rs.getString("cc_expiration");
      } else {
        log("SELECT order failed");
        throw new EJBException("SELECT order failed");
      }
    } catch (Exception e) {
      throw new EJBException(e.getMessage());
    } finally {
        try {
          rs.close();
    ps.close();
          connection.close();
        } catch (Exception e) {
          log(e);
        }
    }
  }

  // データベースに行を保管します。
  public void ejbStore(){
    log("ejbStore()");

    Connection connection = null;
    PreparedStatement ps=null;

    try {
      javax.naming.InitialContext ctx = new 
javax.naming.InitialContext();
      javax.sql.DataSource ds = (javax.sql.DataSource) 
ctx.lookup("compass");
      connection=ds.getConnection();

      ps = connection.prepareStatement("UPDATE orders SET user_id=?, 
trip_id=?, cc_type=?, cc_number=?, cc_expiration=?WHERE 
order_id=?");
      ps.setString(1, userId);
      ps.setInt(2, tripId);
      ps.setString(3, ccType);
      ps.setString(4, ccNumber);
      ps.setString(5, ccExpiration);
      ps.setInt(6, orderId);

      if (ps.executeUpdate() == 1) {
        log("UPDATE order succeeded");
      } else {
        log("UPDATE order failed");
        throw new EJBException("UPDATE order failed");
      }
    } catch (Exception e) {
      log(e);
      throw new EJBException(e.getMessage());
    } finally {
      try {
        ps.close();
        connection.close();
      } catch (Exception e) {
        log(e);
      }
    }
  }

  // データベースから行を削除します。
  public void ejbRemove() throws RemoveException {

    log("ejbRemove()");

    Connection connection = null;
    PreparedStatement ps = null;

    try {
      javax.naming.InitialContext ctx = new 
javax.naming.InitialContext();
      javax.sql.DataSource ds = (javax.sql.DataSource) 
ctx.lookup("compass");
      connection=ds.getConnection();

      ps = connection.prepareStatement("DELETE FROM orders WHERE 
order_id=?");
      ps.setInt(1, orderId);

      if (ps.executeUpdate() == 1) {
        log("DELETE order succeeded");
      } else {
        log("DELETE order failed");
        throw new RemoveException("DELETE order failed");
      }

    } catch (Exception e) {
        log(e);
        throw new EJBException(e.getMessage());
    } finally {
      try {
        ps.close();
        connection.close();
      } catch (Exception e) {
        log(e);
      }
    }
  }

  public void ejbPassivate() {  }
  public void ejbActivate() {  }

  public void setEntityContext(javax.ejb.EntityContext context) {
    this.context = context;
  }

  public void unsetEntityContext() {
    this.context = null;
  }

  // 行を取り出してプライマリキーを返します。
  // 戻り値のタイプが PK のクラスであることに注意します。
  // CMP の場合、このメソッドは void を返します。
  public Integer ejbFindByPrimaryKey(Integer pk) throws 
FinderException {

    log("ejbFindByPrimaryKey()");

    orderId = pk.intValue();

    Connection connection = null;
    PreparedStatement ps=null;
    ResultSet rs=null;

    try {
      javax.naming.InitialContext ctx = new 
javax.naming.InitialContext();
      javax.sql.DataSource ds = (javax.sql.DataSource) 
ctx.lookup("compass");
      connection=ds.getConnection();

      ps = connection.prepareStatement("SELECT count(*) FROM orders 
WHERE order_id=?");
      ps.setInt(1, orderId);

      rs = ps.executeQuery();
      if (rs.next()) {
        throw new javax.ejb.ObjectNotFoundException("Invalid Primary 
Key");
      }
    } catch (Exception e) {
      throw new EJBException(e.getMessage());
    } finally {
      try {
        rs.close();
        ps.close();
        connection.close();
      } catch (Exception e) {
        log(e);
      }
    }

    return pk;
  }

  // 顧客の予約内容を検索します。
  public java.util.Collection ejbFindByCustomer(String userId) throws 
FinderException {

    log("ejbFindByCustomer():"+userId);

    Connection connection = null;
    PreparedStatement ps=null;
    ResultSet rs=null;

    java.util.Vector list = new java.util.Vector();

    try {
      javax.naming.InitialContext ctx = new 
javax.naming.InitialContext();
      javax.sql.DataSource ds = (javax.sql.DataSource) 
ctx.lookup("compass");
      connection=ds.getConnection();

      ps = connection.prepareStatement("SELECT order_id FROM orders 
WHERE user_id=? ORDER BY order_date desc");
      ps.setString(1, userId);

      rs = ps.executeQuery();

      while (rs.next()) {
        Integer pk = new Integer(rs.getInt(1));
        list.addElement(pk);
      }
    } catch (Exception e) {      throw new EJBException(e.getMessage());
    } finally {
      try {
        rs.close();
        ps.close();
        connection.close();
      } catch (Exception e) {
        log(e);
      }
    }
    return list;
  }

  private void log(Object message) {
    System.out.println("-> "+new java.util.Date()+" "+this+" 
"+message);
  }
}

EJB デプロイメントディスクリプタ

BMP エンティティ bean の基本的なデプロイメントディスクリプタには、entity 要素内に要素が含まれます。後で、アプリケーションアセンブラにより、assembly-descriptor 要素内に追加仕様が作成されます。

次のサンプルは、BMP エンティティ bean のデプロイメントディスクリプタ内の要素を示しています。

...
  <entity>
      <ejb-name>Order</ejb-name>
      <home>compass.OrderHomeRemote</home>
      <remote>compass.OrderRemote</remote>
      <ejb-class>compass.OrderBean</ejb-class>
      <persistence-type>Bean</persistence-type>
      <prim-key-class>java.lang.Integer</prim-key-class>
      <reentrant>False</reentrant>
      <security-identity><use-caller-identity/></security-identity>
    </entity>
  </enterprise-beans>
  <assembly-descriptor>
    <container-transaction>
      <method>
        <ejb-name>Order</ejb-name>
        <method-name>*</method-name>
      </method>
      <trans-attribute>Required</trans-attribute>
    </container-transaction>
  </assembly-descriptor>
...

JRun EJB デプロイメントディスクリプタ

次の例は、BMP エンティティ bean の JRun EJB デプロイメントディスクリプタを示しています。

...
    <entity>
      <ejb-name>Order</ejb-name>
      <jndi-name>Order</jndi-name>
    </entity>
...

サンプルクライアント

次のステートレスセッション bean の例は、リモートの BMP エンティティ bean へのクライアントアクセスを示しています。

...
// 新規の予約内容を作成します。
try {
  Object obj = ctx.lookup("Order");
  OrderHomeRemote home = 
(OrderHomeRemote)javax.rmi.PortableRemoteObject.narrow(obj, 
OrderHomeRemote.class);
  OrderRemote order = home.create(custId, tripId, ccType, ccNumber, 
ccExpiration);
  orderId = order.getOrderId();
  log("Order created:"+orderId);
}
  catch (Exception e) {
    log(e);
  }
  // トランザクションが引き続き有効かどうかをチェックします。 
  // 有効でない場合、例外を返してクライアントに通知します。
  if (context.getRollbackOnly()) throw new 
javax.ejb.EJBException("Order failed");

    return orderId;
...

BMP の追加検討事項

フィールドを変更するエンティティ bean のビジネスメソッドの呼び出しの後では必ず、デフォルトで、EJB コンテナは ejbStore メソッドを呼び出します。jrun-ejb-jar.xml の alwaysDirty 要素を true に設定することによって、フィールドの状態にかかわらず、 EJB コンテナ ejbStore メソッドを呼び出すようにすることができます。

alwaysDirty 要素は、CMP bean にも適用されます。