30.3. エンティティ Bean

まず、犯罪ポータルの CMP エンティティ bean の一つを検証しながら、JBoss におけるエンティティ bean について説明します。ローカルの CMP エンティティ bean として実装される gangster bean を検証してみましょう。JBoss は、ローカルのエンティティ bean と同じようにパフォーマンスがアップするようにリモートのエンティティ bean に参照渡しセマンティクスを提供できますが、ローカルの エンティティ bean の使用を強く推奨します。
まず、必要なホームインターフェースから見ていきます。ここでは CMP フィールドのみを集中して検証するため、CMP フィールドを処理するメソッドだけを示します。
// Gangster Local Home Interface
public interface GangsterHome 
    extends EJBLocalHome 
{   
    Gangster create(Integer id, String name, String nickName)
        throws CreateException;
    Gangster findByPrimaryKey(Integer id) 
        throws FinderException; 
}
ローカルインターフェースはクライアントがやり取りに使用するインターフェースです。ここでも CMP フィールドアクセッサーのみを含みます。
// Gangster Local Interface 
public interface Gangster
    extends EJBLocalObject
{
    Integer getGangsterId();

    String getName();

    String getNickName();
    void setNickName(String nickName);

    int getBadness();
    void setBadness(int badness);
}
最後に、実際の gangster bean が入ります。サイズに関わらず、実際に必要なコードは限られます。クラスの大部分は create メソッドです。
// Gangster Implementation Class
public abstract class GangsterBean 
    implements EntityBean 
{
     private EntityContext ctx; 
     private Category log = Category.getInstance(getClass());
     public Integer ejbCreate(Integer id, String name, String nickName)
         throws CreateException 
     {
         log.info("Creating Gangster " + id + " '" + nickName + "' "+ name);
         setGangsterId(id);
         setName(name);
         setNickName(nickName);
         return null;
     }
     
     public void ejbPostCreate(Integer id, String name, String nickName) {
     }
     
     // CMP field accessors ---------------------------------------------
     public abstract Integer getGangsterId();
     public abstract void setGangsterId(Integer gangsterId); 
     public abstract String getName();
     public abstract void setName(String name);
     public abstract String getNickName();
     public abstract void setNickName(String nickName);
     public abstract int getBadness();
     public abstract void setBadness(int badness);
     public abstract ContactInfo getContactInfo();
     public abstract void setContactInfo(ContactInfo contactInfo);  
     //... 
     
     // EJB callbacks ---------------------------------------------------
     public void setEntityContext(EntityContext context) { ctx = context; }
     public void unsetEntityContext() { ctx = null; }
     public void ejbActivate() { }    
     public void ejbPassivate() { }   
     public void ejbRemove() { log.info("Removing " + getName()); }
     public void ejbStore() { }
     public void ejbLoad() { }
}
ここで足りないのは ejb-jar.xml 配備記述子のみです。実際の bean クラスの名前は GangsterBean ですが、このエンティティをここでは GangsterEJB と呼んでいます。
<?xml version="1.0" encoding="UTF-8"?>
<ejb-jar xmlns="http://java.sun.com/xml/ns/"Whats_new_in_JBoss_4-J2EE_Certification_and_Standards_Compliance" version="2.1"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee 
                        http://java.sun.com/xml/ns/j2ee/ejb-jar_\2_1.xsd">
   <display-name>Crime Portal</display-name>

    <enterprise-beans>
        <entity>
            <display-name>Gangster Entity Bean</display-name>
            <ejb-name>GangsterEJB</ejb-name>
            <local-home>org.jboss.cmp2.crimeportal.GangsterHome</local-home>
            <local>org.jboss.cmp2.crimeportal.Gangster</local>

            <ejb-class>org.jboss.cmp2.crimeportal.GangsterBean</ejb-class>
            <persistence-type>Container</persistence-type>
            <prim-key-class>java.lang.Integer</prim-key-class>
            <reentrant>False</reentrant>
            <cmp-version>2.x</cmp-version>
            <abstract-schema-name>gangster</abstract-schema-name>

            <cmp-field>
                <field-name>gangsterId</field-name>
            </cmp-field>
            <cmp-field>
                <field-name>name</field-name>
            </cmp-field>
            <cmp-field>
                <field-name>nickName</field-name>
            </cmp-field>
            <cmp-field>
                <field-name>badness</field-name>
            </cmp-field>
            <cmp-field>
                <field-name>contactInfo</field-name>
            </cmp-field>
            <primkey-field>gangsterId</primkey-field>

            <!-- ... -->
        </entity>
    </enterprise-beans>
</ejb-jar>
これが EJB 2.x CMP エンティティ bean であることを表すために CMP バージョン 2.x を指定しているので注意してください。抽象スキーマ名は gangster に設定しました。「クエリ」で EJB-QL クエリーを確認する際にこれが重要となります。

30.3.1. エンティティマッピング

エンティティの JBoss 設定は jbosscmp-jdbc.xml ファイル内の entity 要素で宣言されます。このファイルは EJB JAR の META-INF ディレクトリに配置され、CMP マッピング設定用の全オプション設定情報を含んでいます。各 bean の entity 要素はトップレベルの jbosscmp-jdbc 要素の配下にある enterprise-beans 要素内にグループ化されます。スタブアウトしたエンティティ設定を以下に示します。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE jbosscmp-jdbc PUBLIC
     "-//JBoss//DTD JBOSSCMP-JDBC 3.2//EN"
     "http://www.jboss.org/j2ee/dtd/jbosscmp-jdbc_3_2.dtd">
<jbosscmp-jdbc>
    <defaults>
        <!-- application-wide CMP defaults -->
    </defaults>
    <enterprise-beans>
        <entity>
            <ejb-name>GangsterEJB</ejb-name>
            <!-- overrides to defaults section -->
            <table-name>gangster</table-name>            
            <!-- CMP Fields (see CMP-Fields) -->
            <!-- Load Groups (see Load Groups)-->
            <!-- Queries (see Queries) -->
        </entity>
    </enterprise-beans>
</jbosscmp-jdbc>
ejb-name 要素は、ejb-jar.xml ファイルにあるものとエンティティの仕様を適合させる必要があります。その要素の残りはグローバルか、又はアプリケーションレベルの CMP デフォルトと bean に特有の CMP マッピング詳細を上書きします。アプリケーションデフォルトは jbosscmp-jdbc.xml ファイルの defaults セクションから出るものであり、グローバルデフォルトは現在のサーバー設定ファイルセットの conf ディレクトリにある defaults セクションから出るものです。 defaults セクションについては、「デフォルト」 で説明があります。図30.3「エンティティ要素のコンテンツモデル」 は完全な entity コンテンツモデルを示しています。
エンティティ要素のコンテンツモデル

図30.3 エンティティ要素のコンテンツモデル

各エンティティの要素の詳細は次の通りになります。
  • ejb-name: この必須要素は、この設定が適用される EJB 名です。この要素は ejb-jar.xml ファイル内のエンティティの ejb-name と一致しなければなりません。
  • datasource: この任意要素はデータソースの検索に使用される jndi-name になります。エンティティまたは関係テーブルで使用されるデータベース接続はすべてデータソースから取得されます。エンティティに対して異なるデータソースを指定すると、finder および ejbSelect がクエリできるドメインを大幅に制約することになるため推奨しません。デフォルトセクションでオーバーライドされない限り java:/DefaultDS がデフォルトです。
  • datasource-mapping: この任意要素は type-mapping名を指定し、 Java タイプが SQL タイプにマッピングされる方法、および EJB-QL 関数がデータベース固有の関数にマッピングされる方法を指定します。タイプのマッピングについては 「マッピング」で説明します。デフォルトセクションでオーバーライドされない限り HypersonicSQL がデフォルトです。
  • create-table: この任意要素が true の場合 JBoss はエンティティに対してテーブルの作成を試行しなければならないことを指定します。アプリケーションがデプロイされると、JBoss はテーブル作成の前にすでにテーブルが存在しているかどうか確認します。テーブルが見つかった場合、ログが記録されるだけでテーブルは作成されません。このオプションは、テーブルの構成が頻繁に変更する開発初期段階で非常に役立ちます。デフォルトセクションでオーバーライドされない限り、デフォルトは false です。
  • alter-table: スキーマの自動作成に create-table が使用される場合、alter-table を使ってエンティティ bean に対する変更を反映しスキーマを最新状態に保つことができます。Alter table は次のような特定のタスクを実行します。
    • 新しいフィールドが作成されます。
    • 使用されなくなったフィールドが削除されます。
    • 宣言されている長さより短い文字列フィールドは宣言されている長さまで延長されます(すべてのデータベースでサポートされているわけではありません)。
  • remove-table: この任意の要素が true の場合、JBoss は各エンティティおよび関連性をマッピングした各関係テーブルに対してテーブルのドロップを試行します。アプリケーションがアンデプロイされると、JBoss はこのテーブルをドロップを試行します。このオプションは、テーブルの構成が頻繁に変更される開発初期段階で非常に役立ちます。デフォルトセクションでオーバーライドされない限り、デフォルトは false です。
  • post-table-create: この任意要素はデータベーステーブルの作成直後に実行されるべき任意の SQL ステートメントを指定します。このコマンドは create-table が true でテーブルが以前に存在しない場合にのみ実行されます。
  • read-only: この任意の要素が true の場合、 bean プロバイダーはいずれのフィールドの値を変更することも許可されないことを指定します。読み取り専用のフィールドはデータベースに格納もしくは挿入されません。プライマリキーフィールドが読み取り専用である場合、create メソッドが CreateExceptionを送出します。set アクセッサーが読み取り専用フィールドで呼び出しされると、EJBException をスローします。読み取り専用フィールドは最後の更新などデータベースのトリガーで入力されるフィールドに便利です。read-only オプションは cmp-field ごとにオーバーライドが可能です。詳しくは 「Read-only フィールド」 で説明します。defaults セクションでオーバーライドされない限り、デフォルトは false です。
  • read-time-out: この任意の要素は読み取り専用フィールドでの読み取りが有効である期間をミリ秒単位で表します。値が 0 の場合、常にトランザクション開始時に値が再ロードされることになり、値が -1 の場合は値がタイムアウトすることがないという意味になります。このオプションも cmp-field ごとにオーバーライドが可能です。read-only が false の場合、この値は無視されます。デフォルトは defaults セクションでオーバーライドされない限り -1 です。
  • row-locking: この任意の要素が true の場合、sJBoss はトランザクションでロードされたすべての列をロックすることを指定します。ほとんどのデータベースがエンティティをロードする際に SELECT FOR UPDATE 構文を使ってこれを実装していますが、実際の構文はこのエンティティで使用されるデータソースマッピング内の row-locking-template で確定されます。defaults セクションでオーバーライドされない限り、デフォルトは false です。
  • pk-constraint: この任意の要素が true の場合、 JBoss はテーブル作成時にプライマリキー制約を追加することを指定します。defaults セクションでオーバーライドされない限り、デフォルトは true です。
  • read-ahead: この任意の要素はエンティティの cmr-fields およびクエリ結果のキャッシュ化を制御します。このオプションについては 「Read-ahead」で説明します。
  • fetch-size: この任意の要素は基礎となるデータストアへの 1 往復で読み込むエンティティ数を指定します。defaults セクションでオーバーライドされない限り、デフォルトは 0 です。
  • list-cache-max: この任意の要素はこのエンティティで追跡可能な read-list 数を指定します。このオプションについては on-load で説明します。defaults セクションでオーバーライドされない限り、デフォルトは 1000 です。
  • clean-read-ahead-on-load: 先行読み込み (read ahead) キャッシュからエンティティがロードされる場合、 JBoss は先行読み込みキャッシュから使用されるデータを削除することができます。デフォルトは false になります。
  • table-name: この任意の要素はこのエンティティ用のデータを保持するテーブル名になります。各エンティティインスタンスはこのテーブルの 1 列内に格納されます。デフォルトは ejb-name になります。
  • cmp-field: この任意の要素で ejb-jar.xmlcmp-field を永続ストア上でマッピングする方法を定義することができます。このオプションについては 「CMP フィールド」で説明します。
  • load-groups: この任意の要素はフィールドのロードグループ化を宣言する CMP フィールドの 1つ以上の グループ化を指定します。このオプションについては 「Load Groups」で説明します。
  • eager-load-groups: このオプションのエレメントは eager load group として 1 つ以上のロードグループ化を定義します。このオプションについては 「一括読み込みプロセス」で説明します。
  • lazy-load-groups: この任意の要素は lazy load group として 1 つ以上のロードグループ化を定義します。これについては 「遅延ローディングプロセス」で説明します。
  • query: この任意の要素は finder と selector の定義を指定します。これについては 「クエリ」で説明します。
  • unknown-pk: この任意の要素により java.lang.Object の未知のプライマリキータイプを永続ストアにマッピングする方法を定義することができます。
  • entity-command: この任意の要素によりエンティティ作成コマンドインスタンスを定義することができます。一般的にこれを使いカスタムのコマンドインスタンスを定義することで、プライマリキー生成を可能にします。これについては 「エンティティコマンドおよびプライマリキー生成」 で詳しく説明しています。
  • optimistic-locking: この任意の要素は楽観的なロッキングに対して使用するストラテジーを定義します。これについては 「楽観的ロッキング」で説明します。
  • audit: この任意の要素は監査予定の CMP フィールドを定義します。詳しくは 「エンティティアクセスの監査」で説明します。