MENU

J2EE環境構築編

J2EE体験編

J2EE体験編
CMRを体験する

CMPを体験したので、次はCMRを体験しましょう。
CMRとはContainer Managed Relationshipの略です。リレーションをコンテナに任せてしまう、というものです。
リレーションとは何かという詳しい説明しません。一言でいえば、2つのテーブルを関連づける、ということです。
分からない人は詳しい友人に聞くか、googleで検索してください。

今回は縦に長いです。今までに比べて理解しておくべきことが格段に多いです。

前提知識

作業を始める前に、いくつか理解しておくべきことがあります。

  • CMRメソッドはローカルインターフェイスを返す
  • ローカルインターフェイスは同一のVMからでないとアクセスできない
  • 同一VMとは、同一のjarファイルに含まれるものを示す

三番目はちょっと正確な表現ではないのかも知れません。
ローカルインターフェイスの実体はローカルヒープ領域への参照らしいので、気になる人は調べてみて下さい。

とにかく、EntityBeanにCMRメソッドを作成しても、直接呼び出すことはできません。
そこで、SessionBeanを一つ作って、SessionBeanにEntityBeanへのローカルインターフェイスを取得させ、クライアントからはSessionBeanを呼び出すことにします。

下準備

リレーションが「2つのテーブルを関連づける」という意味であるなら、CMRを体験するためには2つ(またはそれ以上)のテーブルが必要です。そこで、テーブルを作成しておきます。ここでは、以下のようなテーブルを作成します。

create table customer (id int primary key auto_increment,name text not null,password text not null);
create table masterorder (id int primary key auto_increment,cust_id int not null,item_id int not null,amount int not null,date date);
create table item (id int primary key auto_increment,name text not null,sell_price int not null,stock int not null);

masterorderという奇妙な名前なのは、MySQLではorderが予約語になっていてテーブル名として利用できないからです。
いちいちnot nullを入れていますが、テストであれば省略しておくのも良いでしょう。
そして、これらのテーブルに適当な(正しいという意味ではなく、アトランダムな)データを挿入しておきます。

プロジェクトの追加

これまでの使っていたものへ追加しても良いのですが、ここは手順を確認する上でも、もうひとつのプロジェクトを作成して、そこにBeanやら何やらを追加していきましょう。

注意すべき点はただひとつ、Schema(CMPウィザードの一番上の欄に入力するやつのことです)にそれぞれ別個の名称をつけることです。
同じ名前だと、エラーになります。

命名に規則を定めたのであればそれにしたがって、そうでなければ適当な名前を入力します。
あんまり適当だと後で面倒なことになるかも知れないので、自分なりのルールを決めてしまうのが良いでしょう。命名規則の例は、検索すれば結構見つかると思います。
フィールド名、コラム名、プライマリキーなど、作成したテーブルに合うように入力してBeanを作成していきましょう。

Beanの作成が終わったら、EJBモジュールを作成して、Beanを追加します。
各Beanのidというフィールドはauto_incrementを利用するので、その設定も行います。
xdoclet.xmlの変更も忘れてはいけません。
下準備だけでも結構な手間です。

メソッドの追加

どのBeanに追加するか、という点が重要な意味を持ちますが、実際にはテーブルを定義した時点でほとんど決まっています。分からない人は、「正規化 データベース」というキーワードで検索してみると良いでしょう。きっとヒントが見つかります。

さて、作業を説明します。
パッケージエクスプローラのCustomerBean.javaを右クリックして、Lomboz J2EE、Add EJB Methodを選択します。
signatureは

public abstract java.util.Collection getOrderByName()

です。
BusinessとLocalにチェックを入れるのを忘れないようにしましょう。
これで

/**
 * @ejb.interface-method
 *      view-type="local"
 *
 **/
public abstract java.util.Collection getOrderByName();

こんな感じのソースができているはずです。
このコメント部分に、タグを追加します。

 * @ejb.relation name = "CustToOrder"
 *      target-ejb = "MasterOrder" 
 *      role-name = "CustToOrder"
 *      target-multiple = "no"
 *      target-role-name = "OrderToCust"
 *      target-cascade-delete = "no"
 *      
 *
 * @jboss.target-relation related-pk-field = "id"
 *      fk-column = "cust_id"

こんな感じ。
この中で、nameで終っているものについては、任意の文字列で良いと思います。
target-multipleは「1」の側か「many」の側かという設定です。
これらの記述はejb-jar.xmlとjbosscmp-jdbc.xmlに反映されます。

まず、ejb-jar.xmlの関連する部分です。

   <!-- Relationships -->
   <relationships >
      <ejb-relation >
         <ejb-relation-name>CustToOrder</ejb-relation-name>

         <ejb-relationship-role >
            <ejb-relationship-role-name>CustToOrder</ejb-relationship-role-name>
            <multiplicity>One</multiplicity>
            <relationship-role-source >
               <ejb-name>Customer</ejb-name>
            </relationship-role-source>
            <cmr-field >
               <cmr-field-name>orderByName</cmr-field-name>
               <cmr-field-type>java.util.Collection</cmr-field-type>
            </cmr-field>
         </ejb-relationship-role>

         <ejb-relationship-role >
            <ejb-relationship-role-name>OrderToCust</ejb-relationship-role-name>
            <multiplicity>Many</multiplicity>
            <relationship-role-source >
               <ejb-name>MasterOrder</ejb-name>
            </relationship-role-source>
         </ejb-relationship-role>

      </ejb-relation>
        <!-- 
          To add relationships for beans not managed by XDoclet, add
          a file to your XDoclet merge directory called relationships.xml that contains
          the <ejb-relation></ejb-relation> markups for those beans.
        --> 
   </relationships>

CustomerBean.javaに加えたタグをいろいろ変えてみると、この記述が変化します。
もちろん、正しくないとデプロイ時か実行時にエラーになりますが、実験してみるのも面白いでしょう。

次にjbosscmp-jdbc.xmlです。

  <relationships>
    <ejb-relation>
      <ejb-relation-name>CustToOrder</ejb-relation-name>

      <ejb-relationship-role>
          <ejb-relationship-role-name>CustToOrder</ejb-relationship-role-name>
          <key-fields>
             <key-field>
               <field-name>id</field-name>
               <column-name>cust_id</column-name>
             </key-field>
          </key-fields>

      </ejb-relationship-role>
      <ejb-relationship-role>
          <ejb-relationship-role-name>OrderToCust</ejb-relationship-role-name>
                  <key-fields/>

      </ejb-relationship-role>
    </ejb-relation>
     <!-- 
       To add jboss relationships for beans not managed by XDoclet, add
       a file to your XDoclet merge directory called jbosscmp-jdbc-relationships.xml that contains
       the <ejb-relation></ejb-relation> markups for those beans.
     --> 
  </relationships>

これも、見ればどのタグがどの記述に対応しているか、大体分かると思います。

SessionBeanの作成

最初の方で説明した通り、cmrメソッドは直接アクセスできないので、SessionBeanを介して間接的にアクセスする仕組みを用意します。
パッケージエクスプローラのソースパッケージを右クリックして、新規、Lomboz EJB Creation Wizardを選択します。
適当な名前をつけて、Statelessを選んだら完成です。
このBeanにメソッドを二つ追加します。
一つはejbCreate()、もう一つがcmrメソッドへのアクセサ(という用語でいいのでしょうか?)です。

パッケージエクスプローラでsessionBeanを右クリックして、Lomboz j2ee、Add ejb methodを選びます。

public void ejbCreate() throws javax.ejb.CreateException

というsignatureで作成します。
このとき、createとremoteにチェックを入れるのを忘れないようにします。
すると、こんな感じのソースができます。

public abstract class ShopSessionBean implements javax.ejb.SessionBean {
/**
 * @ejb.create-method
 *      view-type="remote" 
**/
public void ejbCreate(Integer id) throws javax.ejb.CreateException{ 
}
}

これにいろいろ追加して、

public abstract class ShopSessionBean implements javax.ejb.SessionBean {
private CustomerLocalHome clh;
/**
 * @ejb.create-method
 *      view-type="remote" 
**/
public void ejbCreate() throws javax.ejb.CreateException{ 
        
        InitialContext ctx;
        
        try{
                ctx=new InitialContext();
                clh=(CustomerLocalHome)ctx.lookup(CustomerLocalHome.JNDI_NAME);
        }catch (NamingException e){
                e.printStackTrace();
        }
        
}
}

これでcreateは出来上がりです。

次に、getメソッドを追加します。

public java.util.Collection getOrderByName(Integer id)

というsignatureで作成します。
business、remoteにチェックを入れておきます。
すると

/**
 * @ejb.interface-method
 *      view-type="remote" 
**/
public java.util.Collection getOrderByName(Integer id){
}

こんな感じのメソッドの雛型が作成されます。
これにいろいろ追加して

/**
 * @ejb.interface-method
 *      view-type="remote" 
**/
public java.util.Collection getOrderByName(Integer id){
        
        ArrayList al=new ArrayList();

        try{
                
                Iterator i=clh.findByPrimaryKey(id).getOrderByName().iterator();
                
                while(i.hasNext()){
                        al.add(i.next());
                }
                
        }catch (FinderException e){
                
                e.printStackTrace();

        }
        
        return al;
        
}

とします。
これでほとんど出来上がりなのですが、setメソッドを用意しておかないとデプロイの時にエラーになるので、中身の無いメソッドを用意しておきます。

/**
 * @ejb.interface-method
 *      view-type="remote" 
**/
public void setOrderByName(){ 
}

これでSessionBaenは出来上がりです

テスト

Lombozに用意されているテスト用クライアント作成Wizardを利用して、きちんと動くかどうか、実験してみます。

パッケージエクスプローラの適当な場所で右クリックして、新規、lomboz ejb test client wizardを選択します。 ダイアログが表示されるので、先ほど作成しておいたSessionBeanを入力します。
ソースには既にcreate()まで書き込まれているので、先ほど作成したメソッドをその後ろに書き込むだけです。

        public void testBean() {

                try {
                        shop1.ShopSession myBean = getHome().create();
                        //--------------------------------------
                        //This is the place you make your calls.
                        //System.out.println(myBean.callYourMethod());

                        Integer i=new Integer(1);
                        java.util.Collection c=myBean.getOrderByName(i);
                        System.out.println(c);

                } catch (RemoteException e) {
                        e.printStackTrace();
                } catch (CreateException e) {
                        e.printStackTrace();
                } catch (NamingException e) {
                        e.printStackTrace();
                }

こんな感じ。
これを実行すると、コンソールに何かが表示されるはずです。
何か、などという妙な表現なのは、場合によっては例外が生じてスタックトレースが表示されたり、[]しか表示されなかったりするからです。
例外になるのは、customerテーブルのid=1に対応する行がない場合です。
[]しか表示されないのは、masterorderテーブルにcust_id=1の行がない場合です。
対応する行が存在する場合は、

[CustmerLocal]

のような表示になります。
さて、ここまで根気強く読んでくれた方の中には既に気づいている方もおられることと思いますが、このコードは何の役にも立ちません。
クライアントからローカルインターフェイスに直接アクセスすることはできないからです。
何か有益な作業をさせたいなら、SessionBeanにデータを操作させた上で、クライアント側に返すことになるでしょう。