この章では、コンテナ管理の永続性を持つEJB 2.0エンティティBeanを使用するために構成が必要な各種のオプションについて説明します。
表5-1にこれらのオプションを一覧表示し、基本オプション(ほとんどのアプリケーションに適用可能)であるか、拡張オプション(より専門的なアプリケーションに適用可能)であるかを示します。
表5-1 コンテナ管理の永続性を持つEJB 2.0エンティティBeanに対する構成可能オプション
| オプション | タイプ |
|---|---|
|
|
基本 |
|
|
基本 |
|
|
基本 |
|
|
拡張 |
|
|
拡張 |
|
|
拡張 |
|
コンテナ管理の永続性を持つEJB 2.0エンティティBeanに対するコールバック・メソッドの構成 |
拡張 |
|
注意: コンテナ管理の永続性を持つエンティティBeanの構成では、JDeveloper IDEを使用することをお薦めします。JDeveloperにエンティティBeanとデータベース表の間の複雑なマッピングを管理する機能があることがその理由です。詳細は、「コンテナ管理の関連フィールドの構成」と「JDeveloperの使用」を参照してください。 |
詳細は次のトピックを参照してください。
各エンティティBeanインスタンスには、他のインスタンスとは一意に区別される主キーがあります。主キー(または複合主キー内に含まれるフィールド)は、デプロイメント・ディスクリプタ内のコンテナ管理の永続フィールドとして宣言する必要があります。
この項では主キー構成の次の内容について説明します。
主キー内のすべてのフィールドはプリミティブ、シリアライズ可能、またはSQL型にマップ可能な型のいずれかに制限されます。主キーは次のどちらかの方法で定義できます。
主キーの型を既知の型に定義します。この型はデプロイメント・ディスクリプタの<prim-key-class>要素で定義されます。永続主キーとして識別されるデータ・フィールドは、デプロイメント・ディスクリプタの<primkey-field>要素で識別されます。Beanクラス内で宣言される主キー変数はpublicとして宣言する必要があります。
主キー定義の拡張オプションとは、その型を、シリアライズ可能な<NAME>PKクラス内のシリアライズ可能オブジェクトとして定義することです。このクラスはデプロイメント・ディスクリプタの<prim-key-class>で宣言されます。詳細は「主キー・クラスの構成」を参照してください。
自動的に生成される主キーを指定します。<prim-key-class>でjava.lang.Objectを主キー・クラス型として指定する場合に、主キー名を<primkey-field>で指定しないと、主キーは自動的にコンテナで生成されます。詳細は「主キーの自動生成の構成」を参照してください。
例5-1 デプロイメント・ディスクリプタ内における既知の型の主キーの定義
単純なCMPについて、デプロイメント・ディスクリプタ内で主キーのデータ型を定義することで主キーを既知の型として定義できます。例5-1は主キー(従業員番号)をjava.lang.Integerとして定義する例を示しています。
<enterprise-beans>
<entity>
<display-name>Employee</display-name>
<ejb-name>EmployeeBean</ejb-name>
<local-home>employee.EmployeeLocalHome</local-home>
<local>employee.EmployeeLocal</local>
<ejb-class>employee.EmployeeBean</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>Employee</abstract-schema-name>
<cmp-field><field-name>empNumber</field-name></cmp-field>
<cmp-field><field-name>empName</field-name></cmp-field>
<cmp-field><field-name>salary</field-name></cmp-field>
<primkey-field>empNumber</primkey-field>
</entity>
...
</enterprise-beans>
一度主キーを定義すると、コンテナが主キーに対するエンティティBean表の列を作成し、デプロイメント・ディスクリプタで定義された主キーをこの列にマップします。
orion-ejb-jar.xmlファイル内で、ejb-jar.xmlファイルで定義されたコンテナ管理の永続フィールドまたは主キー・フィールドをデータベース列名にマップすることで、主キーが基底となるデータベース永続記憶域にマップされます。次のorion-ejb-jar.xmlフラグメントでは、jdbc/OracleDSデータ・ソースで定義されるデータベースでEmpBean永続記憶域がEMP表として定義されます。<entity-deployment>要素の定義(表A-1「<entity-deployment>要素の属性」を参照)に従って、主キーのempNumberがEMP表のEMPNUMBER列にマップされ、empNameおよびsalary永続フィールドがEMP表でそれぞれEMPNAMEおよびSALARY列にマップされます。
<entity-deployment name="EmpBean" ...table="EMP" data-source="jdbc/OracleDS"...>
<primkey-mapping>
<cmp-field-mapping name="empNumber" persistence-name="EMPNUMBER" />
</primkey-mapping>
<cmp-field-mapping name="empName" persistence-name="EMPNAME" />
<cmp-field-mapping name="salary" persistence-name="SALARY" />
...
主キーが単純なデータ型よりも複雑な場合、主キーは<NAME>PKというシリアライズ可能なクラスである必要があります。主キー・クラスはデプロイメント・ディスクリプタの<prim-key-class>要素で定義します。
主キー変数は次の条件に従う必要があります。
デプロイメント・ディスクリプタの<cmp-field><field-name>要素で定義されること。これによってコンテナが主キー・フィールドを管理できます。
Beanクラス内でpublicとして宣言され、プリミティブ、シリアライズ可能、またはSQL型にマップ可能な型のいずれかに制限されること。
主キーを構成する変数の名前が、<cmp-field><field-name>要素と主キー・クラスの両方で同じであること。
主キー・クラス内で、ユーザーは主キー・インスタンスを作成するためのコンストラクタを実装します。主キー・クラスをこの方法で一度定義すると、コンテナがクラスを管理します。
例5-2 主キー・クラスの定義
例5-2では主キー・クラス内に従業員番号が置かれています。
package employee;
public class EmployeePK implements java.io.Serializable {
public Integer empNumber;
public EmployeePK() {
this.empNumber = null;
}
public EmployeePK(Integer newEmpNumber) {
this.empNumber = newEmpNumber;
}
}
例5-3 デプロイメント・ディスクリプタの主キー・クラスの宣言
例5-3のように、主キー・クラスはデプロイメント・ディスクリプタの<prim-key-class>要素内で宣言され、その各変数は<cmp-field><field-name>要素内で宣言されます。
<enterprise-beans>
<entity>
<description>no description</description>
<display-name>EmployeeBean</display-name>
<ejb-name>EmployeeBean</ejb-name>
<local-home>employee.EmployeeLocalHome</local-home>
<local>employee.EmployeeLocal</local>
<ejb-class>employee.EmployeeBean</ejb-class>
<persistence-type>Container</persistence-type>
<prim-key-class>employee.EmployeePK</prim-key-class>
<reentrant>False</reentrant>
<cmp-version>2.x</cmp-version>
<abstract-schema-name>Employee</abstract-schema-name>
<cmp-field><field-name>empNumber</field-name></cmp-field>
<cmp-field><field-name>empName</field-name></cmp-field>
<cmp-field><field-name>salary</field-name></cmp-field>
</entity>
...
</enterprise-beans>
ユーザーが一度主キーを定義すると、コンテナが主キーに対するエンティティBean表の列を作成し、デプロイメント・ディスクリプタで宣言された主キー・クラスをこの列にマップします。
永続フィールドは、「主キーの構成」で説明されているのと同じ方法でorion-ejb-jar.xmlにマップされます。複合主キーでは、マッピングに複数のフィールドが含まれるため、<primkey-mapping>要素の<cmp-field-mapping>要素には別の<fields>下位要素が含まれます。主キーのあらゆるフィールドは、<fields>要素の別々の<cmp-field-mapping>要素で次のように定義されます。
<primkey-mapping>
<cmp-field-mapping>
<fields>
<cmp-field-mapping name="empNumber" persistence-name="EMPNUMBER" />
</fields>
</cmp-field-mapping>
</primkey-mapping>
EJB 2.0仕様では、エンティティBeanに対する主キーはejbCreateメソッド内で初期化する必要があります。ただし、このメソッドではこのBeanの別のBeanとの関連を設定できません。外部キーでこの関連を最も早く設定できるのは、ejbPostCreateメソッドです。
つまり、複合主キー内に外部キーがある場合、ユーザーは、ejbCreateメソッドで複合主キー内のすべてのフィールドを設定する必要があるにもかかわらず、このメソッドでは外部キーを設定できないという問題に直面します。
次の仮説シナリオではこの問題の回避策をモデル化します。このシナリオでは、ある注文に1つ以上の商品が含まれる可能性があり、注文Beanの中には多数の商品があり、各商品は各注文に属します。商品に対する主キーは、商品識別子と注文識別子で構成される複合主キーです。注文識別子は注文を示す外部キーです。
ユーザーは実際の外部キー・フィールドを模倣するプレースホルダ永続フィールドを追加するため、デプロイメント・ディスクリプタとBean実装を変更する必要があります。このフィールドはejbCreateメソッドの際に設定されます。ただし、プレースホルダ永続フィールドと外部キーは同じデータベース列を示します。実際の外部キーはejbPostCreateメソッドの際に更新されます。
|
注意: ejb-jar.xmlファイルをプレースホルダ永続フィールドと外部キーで変更します。表を作成せずにorion-ejb-jar.xmlファイルを自動的に生成するため、orion-application.xmlファイルの<autocreate-tables>要素をfalseに設定してアプリケーションをデプロイします。次に、正しいデータベース列を示すようにorion-ejb-jar.xmlファイルを変更し、<autocreate-tables>要素をtrueに設定してから再デプロイします。 |
例5-4 主キー内に外部キーを内包するためのデプロイメント・ディスクリプタとBeanコードの変更
例5-4はデプロイメント・ディスクリプタとBean実装の両方の変更例を示しています。
注文シナリオでは、各注文に1つ以上の商品が含まれます。OrderBeanは注文を表し、OrderItemBeanは注文の商品を表します。各商品には、商品番号と、その商品が属する注文番号で構成される主キーがあります。つまり、商品に対する主キーには注文Beanを示す外部キーが含まれます。
複合主キーに対する調整を行うには、ejb-jar.xmlファイルを次のように変更します。
主キーの永続フィールドを外部キーに対するプレースホルダとして定義します。このプレースホルダは複合主キー・クラス定義で使用されます。
例5-4で、orderId永続フィールドは<cmp-field>要素で定義されます。orderIdおよびitemId永続フィールドは、OrderItemPK.javaにおける複合主キーの識別に使用されます。
主キー定義外の外部キーを、<relationships>セクションの<cmr-field>要素で定義します。
例5-4で、belongToOrder外部キーはOrderItemBeanに対する<cmr-field>要素で定義され、商品から注文への関係を定義します。
<entity>
<ejb-name>OrderItemBean</ejb-name>
<local-home>OrderItemLocalHome</local-home>
<local>OrderItemLocal</local>
<ejb-class>OrderItemBean</ejb-class>
...
<cmp-field><field-name>itemId</field-name></cmp-field>
<cmp-field><field-name>orderId</field-name></cmp-field>
<cmp-field><field-name>price</field-name></cmp-field>
<prim-key-class>OrderItemPK</prim-key-class>
...
</entity>
<relationships>
<ejb-relation>
<ejb-relation-name>Order-OrderItem</ejb-relation-name>
<ejb-relationship-role>
<ejb-relationship-role-name>
Order-Has-OrderItems
</ejb-relationship-role-name>
<multiplicity>One</multiplicity>
<relationship-role-source>
<ejb-name>OrderBean</ejb-name>
</relationship-role-source>
<cmr-field>
<cmr-field-name>items</cmr-field-name>
<cmr-field-type>java.util.Collection</cmr-field-type>
</cmr-field>
</ejb-relationship-role>
<ejb-relationship-role>
<ejb-relationship-role-name>
OrderItems-from-Order
</ejb-relationship-role-name>
<multiplicity>Many</multiplicity>
<cascade-delete/>
<relationship-role-source>
<ejb-name>OrderItemBean</ejb-name>
</relationship-role-source>
<cmr-field>
<cmr-field-name>belongToOrder</cmr-field-name>
</cmr-field>
</ejb-relationship-role>
...
OrderItemPK.javaクラスは複合主キーの内容を次のように定義します。
public class OrderItemPK implements java.io.Serializable {
public Integer itemID;
public Integer orderID;
public OrderItemPK() {
this.itemId = null;
this.orderId = null;
}
public OrderItemPK(Integer newItemId, Integer newOrderId) {
this.itemId = newItemId;
this.orderId = newOrderId;
}
public boolean equals(Object o) {
if (o instanceof OrderItemPK) {
OrderItemPK pk = (OrderItemPK) o;
if (pk.itemId.intValue() == itemId.intValue() &&
pk.orderId.intValue() == orderId.intValue()) {
return true;
}
}
return false;
}
public int hashCode() {
return itemId.hashCode() * orderId.hashCode();
}
}
自動作成されるデータベース表で十分な場合は、orion-ejb-jar.xmlファイルを変更する必要はありません。ただし、既存のデータベース表にマップする必要がある場合は、その表を指すようにorion-ejb-jar.xmlファイルを変更する必要があります(詳細は「関連フィールドのデータベースに対する明示的マッピングの構成」を参照)。
orion-ejb-jar.xmlファイルの自動生成後、これをユーザーの開発ディレクトリにコピーします。データベース列名は、それぞれの永続フィールド名および関連フィールド名マッピングのpersistence-name属性で定義されます。プレースホルダ永続フィールドと外部キーの両方に対するpersistence-name属性は、必ず同じものにします。
Order/OrderItem例に対するorion-ejb-jar.xmlファイルを次に示します。
<entity-deployment name="OrderItemBean" table="ORDER_ITEM"> <primkey-mapping> <cmp-field-mapping name="itemId" persistence-name="Item_ID" /> <cmp-field-mapping name="orderId" persistence-name="Order_ID" /> </primkey-mapping> <cmp-field-mapping name="price" persistence-name="Price" /> <cmp-field-mapping name="belongToOrder"> <entity-ref home="OrderBean"> <cmp-field-mapping name="belongToOrder" persistence-name="Order_ID" /> </entity-ref> </cmp-field-mapping> </entity-deployment> <entity-deployment name="OrderBean" table="ORDER"> <primkey-mapping> <cmp-field-mapping name="orderId" persistence-name="Order_ID" /> </primkey-mapping> <cmp-field-mapping name="orderDesc" persistence-name="Order_Description" /> <cmp-field-mapping name="items"> <collection-mapping table="ORDER_ITEM"> <primkey-mapping> <cmp-field-mapping name="OrderBean_orderId"> <entity-ref home="OrderBean"> <cmp-field-mapping name="OrderBean_orderId"> </entity-ref> </cmp-field-mapping> </primkey-mapping> <value-mapping type="OrderItemLocal"> <cmp-field-mapping name="OrderItemBean_itemId"> <entity-ref home="OrderItemBean"> <cmp-field-mapping name="OrderItemBean_itemId"> <cmp-field-mapping name="OrderItemBean_itemId"> </fields> </cmp-field-mapping> </entity-ref> </cmp-field-mapping> </value-mapping> </collection-mapping> </cmp-field-mapping> </entity-deployment>
Order/OrderItemの例において、orion-ejb-jar.xmlファイルのOrderItemBeanに対する<entity-deployment>(表A-1「<entity-deployment>要素の属性」を参照)セクションでは、次のことが行われます。
表がtable属性で定義されます。この例ではORDER_ITEMです。
itemIdの列名はpersistence-name属性でItem_IDとして定義されます。
プレースホルダ永続フィールドのorderIdの列名は、persistence-name属性でOrder_IDとして定義されます。
外部キーのbelongToOrderは、プレースホルダ永続フィールドのorderIdと同じ列の、Order_IDデータベース列にマップされます。
外部キー(belongToOrder)とプレースホルダ永続フィールド(orderId)は両方とも同じデータベース列を示します。
最後に、プレースホルダ永続フィールドおよび外部キーの双方と連携するように、Bean実装を次のように更新します。
ejbCreateメソッドで、次の作業を実行します。
外部キー・フィールドのかわりとなるプレースホルダ永続フィールドを作成します。
ejbCreateメソッドでプレースホルダ永続フィールドに値を設定します。この値はデータベース表の外部キー・フィールドに書き込まれます。
ejbPostCreateメソッドで、外部キーを重複した永続フィールドの値に設定します。
|
注意: 外部キーは主キーの一部であるため、設定できるのは1回のみです。 |
Order/OrderItemの例で、orderId永続フィールドはejbCreateメソッドで設定され、belongToOrder関連フィールドはejbPostCreateメソッドで設定されます。
public OrderItemPK ejbCreate(OrderItem orderItem) throws CreateException {
setItemId(orderItem.getItemId());
setOrderId(orderItem.getOrderId());
setPrice(orderItem.getPrice());
return new OrderItemPK(orderItem.getItemId(),orderItem.getOrderId());
}
public void ejbPostCreate(OrderItem orderItem) throws CreateException {
// right after the bean has been created
try {
Context ctx = new InitialContext();
OrderLocalHome orderHome =
(OrderLocalHome)ctx.lookup("java:comp/env/OrderBean");
OrderLocal order = orderHome.findByPrimaryKey(orderItem.getOrderId());
setBelongToOrder(order);
}
catch(Exception e) {
e.printStackTrace();
throw new EJBException(e);
}
}
ejbCreateおよびejbPostCreateメソッドに渡されるOrderItemオブジェクトに対するコードを次に示します。
public class OrderItem implements java.io.Serializable {
private Integer itemId;
private Integer orderId;
private Double price;
public OrderItem(Integer itemId, Integer orderId, Double price) {
this.itemId = itemId;
this.orderId = orderId;
this.price = price;
}
public Integer getItemId() {
return itemId;
}
public void setItemId(Integer itemId) {
this.itemId = itemId;
}
public Integer getOrderId() {
return orderId;
}
public void setOrderId(Integer orderId) {
this.orderId = orderId;
}
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
this.price = price;
}
public boolean equals(Object other) {
if (other instanceof OrderItem) {
OrderItem orderItem = (OrderItem)other;
if (itemId.equals(orderItem.getItemId()) &&
orderId.equals(orderItem.getOrderId()) &&
price.equals(orderItem.getPrice()) ) {
return true;
}
}
return false;
}
}
デプロイメント・ディスクリプタの<prim-key-class>要素でjava.lang.Objectを主キー・クラス型として指定する場合に、主キー名を<primkey-field>要素で指定しないと、主キーは自動的にコンテナで生成されます。
<enterprise-beans>
<entity>
<display-name>Employee</display-name>
<ejb-name>EmployeeBean</ejb-name>
<local-home>employee.EmployeeLocalHome</local-home>
<local>employee.EmployeeLocal</local>
<ejb-class>employee.EmployeeBean</ejb-class>
<persistence-type>Container</persistence-type>
<prim-key-class>java.lang.Object</prim-key-class>
<reentrant>False</reentrant>
<cmp-version>2.x</cmp-version>
<abstract-schema-name>Employee</abstract-schema-name>
<cmp-field><field-name>empNumber</field-name></cmp-field>
<cmp-field><field-name>empName</field-name></cmp-field>
<cmp-field><field-name>salary</field-name></cmp-field>
</entity>
...
</enterprise-beans>
一度主キーを定義すると、コンテナがLONG型の主キーに対するエンティティBean表のautoidという列を作成します。コンテナは主キー値に乱数を使用します。これはBeanのorion-ejb-jar.xmlで次のように生成されます。
<primkey-mapping>
<cmp-field-mapping name="auto_id" persistence-name="autoid"/>
</primkey-mapping>
コンテナ管理の永続フィールドは、データベース表に永続する単純なデータ型を表します。これらのフィールドはエンティティBeanの状態を定義します。これらのフィールドはBeanの直接属性です。永続フィールドの詳細は、「コンテナ管理の永続フィールド」を参照してください。
コンテナ管理の永続性を持つエンティティBeanで、次の方法でBeanインスタンスとデプロイメント・ディスクリプタの両方に永続データを定義します。
Beanインスタンスのアクセッサ・メソッド(getterおよびsetter): 各永続フィールドに対して、getterおよびsetterメソッドの両方が作成されます。getterから返されてsetterに渡されたパラメータのデータ型はフィールドの単純なデータ型を定義します。フィールドの名前はgetterおよびsetterメソッドの名前で指定されます。
次のコードは従業員名の永続フィールドに対するgetterおよびsetterを表しています。getterから返されてsetterに渡されたStringはフィールドの単純なデータ型です。メソッド名から「get」および「set」を削除して最初の文字を大文字から小文字にすると、永続フィールド名になります。次の例ではempNameが永続フィールド名です。
public abstract String getEmpName() throws RemoteException; public abstract void setEmpName(String empName) throws RemoteException;
デプロイメント・ディスクリプタでこれらのフィールドを永続的であると宣言します。各フィールド名は、デプロイメント・ディスクリプタの<cmp-field><field-name>要素で定義する必要があります。前述した例では、empNumber、empNameおよびsalaryの3つの永続フィールドがデータ・アクセッサ・メソッドで定義されます。
これらのフィールドは、ejb-jar.xmlデプロイメント・ディスクリプタの<cmp-field><field-name>要素で、次のように永続フィールドとして定義されます。
<enterprise-beans>
<entity>
<display-name>Employee</display-name>
<ejb-name>EmployeeBean</ejb-name>
<local-home>employee.EmployeeLocalHome</local-home>
<local>employee.EmployeeLocal</local>
<ejb-class>employee.EmployeeBean</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>Employee</abstract-schema-name>
<cmp-field><field-name>empNumber</field-name></cmp-field>
<cmp-field><field-name>empName</field-name></cmp-field>
<cmp-field><field-name>salary</field-name></cmp-field>
<primkey-field>empNumber</primkey-field>
</entity>
...
</enterprise-beans>
これらのフィールドを、データベースに次のようにマップします。
これらのフィールドに対するデフォルトを使用して、デプロイメント・ディスクリプタのそれ以上の構成を回避します。詳細は「永続フィールドのデータベースに対するデフォルト・マッピングの構成」を参照してください。
永続データ・フィールドを、指定されたデータベースに存在する表内の列にマップします。永続データ・マッピングがorion-ejb-jar.xmlファイル内で構成されます。詳細は「永続フィールドのデータベースに対する明示的マッピングの構成」を参照してください。
ejb-jar.xmlファイルで永続フィールドを単純に定義する場合、OC4Jではこれらのフィールドのデータベースに対する次のようなマッピングが提供されます。
データベース: 使用中のOC4Jインスタンス構成に設定されているデフォルトのデータベース。JNDI名については、エミュレートされたデータ・ソースには<location>要素を、エミュレートされていないデータ・ソースには<ejb-location>要素を使用します。
インストール後の状態では、デフォルト・データベースはローカルにインストールされたOracleデータベースで、ORCLというSIDでポート1521でリスニングするデータベースです。デフォルト・データベースをカスタマイズするには、最初に構成されたデータベースを、ユーザーのデータベースを指すように変更します。
表: コンテナは、表名の一意性が保証されたデフォルト表を自動的に作成します。今後の再デプロイの場合に備えて、この表名で生成されたorion-ejb-jar.xmlファイルを、ejb-jar.xmlファイルと同じディレクトリにコピーします。これによって、今後の再デプロイの際に最初に生成された表名と同じ名前を使用できます。このファイルをコピーしないと、異なる表名が生成される可能性があります。
表名は次の名前で構成され、それぞれの名前はアンダースコア文字( _ )で区切られます。
デプロイメント・ディスクリプタの<ejb-name>で定義されるEJB名。
.jar拡張子を含むJARファイル名。ただし、SQL表記規則に従ってダッシュ文字( - )およびピリオド( . )はすべてアンダースコア文字( _ )に変換されます。たとえば、JARファイルの名前がemployee.jarの場合は、employee_jarが名前に付加されます。
デプロイ時にユーザーが定義したアプリケーション名。
構成された名前が30文字を超えている場合、この名前は24文字で切り捨てられます。その後に、英数字ハッシュ・コードで構成される6文字が名前に付加されます。
たとえば、EJB名がEmpBeanで、JARファイルがempl.jarで、アプリケーション名がemployeeの場合、デフォルトの表名はEmpBean_empl_jar_employeeになります。
列名: エンティティBean表の各列は指定されたデータベースの<cmp-field>要素と同じ名前を持ちます。Javaデータ型をデータベース・データ型に変換した、データベースに対するデータ型は、oracle.xmlなどの特定のデータベースXMLファイルで定義されます。
「永続フィールドのデータベースに対するデフォルト・マッピングの構成」で説明したように、ユーザーの永続データはコンテナを使用してデータベース表に自動的にマップできます。ただし、Beanで表されているデータがより複雑な場合、またはOC4Jで提供されるデフォルトを使用しない場合は、永続データをorion-ejb-jar.xmlファイル内の既存のデータベース表およびその列にマップすることができます。フィールドが一度マップされると、コンテナは永続データの永続記憶域を指定された表および行に提供します。
永続フィールドをデータベースに明示的にマップするには、次の手順を実行します。
ejb-jar.xml要素のみが構成されたアプリケーションをデプロイします。
OC4Jはデフォルト・マッピングが含まれるユーザー用のorion-ejb-jar.xmlファイルを作成します。これらのフィールドは作成するよりも変更する方が簡単です。
ユーザーが指定したデータベース表および列を使用するように、orion-ejb-jar.xmlファイルの<entity-deployment>要素(表A-1「<entity-deployment>要素の属性」を参照)を変更します。
<cmp-field>要素内でそれぞれにコンテナ管理の永続フィールドを一度定義すると、各フィールドを特定のデータベース表および列にマップできます。このように、これらの永続フィールドを既存のデータベース表にマップできます。マッピングはorion-ejb-jar.xmlファイル(OC4J固有デプロイメント・ディスクリプタ)で発生します。
永続フィールドの明示的マッピングは<entity-deployment>要素(表A-1「<entity-deployment>要素の属性」を参照)内で完結します。この要素にはエンティティBeanに対するすべてのマッピングが含まれます。永続フィールド・マッピングに固有の属性および要素を次に示します。
<entity-deployment name="..." location="..." table="..." data-source="...">
<primkey-mapping>
<cmp-field-mapping name="..." persistence-name="..." />
</primkey-mapping>
<cmp-field-mapping name="..." persistence-name="..." />
...
</entity-deployment>
表5-2 特定のデータベース表へ永続フィールドをマッピングするためのデプロイメント・ディスクリプタの要素
| 要素または属性の名前 | 説明 |
|---|---|
|
|
|
|
|
JNDIロケーション。 |
|
|
データベース表名。 |
|
|
表が存在するデータベースに対するデータ・ソース。 |
|
|
主キーを表にマップする方法の定義。 |
|
|
|
orion-ejb-jar.xmlファイル内で次のように構成を行います。
マップ対象のコンテナ管理の永続フィールドを含むすべてのエンティティBeanに対して<entity-deployment>要素を構成します。
マップするBean内のすべてのフィールドに対して<cmp-field-mapping>要素を構成します。各<cmp-field-mapping>要素には永続対象のフィールドの名前を次のように含む必要があります。
<primkey-mapping>要素の<cmp-field-mapping>要素で主キーを構成します。
単一の<cmp-field-mapping>要素内で単一フィールドにマップされる単純なデータ型(プリミティブ、単純なオブジェクト、またはシリアライズ可能オブジェクトなど)を構成します。名前およびデータベース・フィールドはすべて要素の属性内で定義されます。
例5-5 永続フィールドの特定データベース表へのマッピング
例5-5は、従業員永続フィールドをOracleデータベース表のEMPにマップして、Beanインスタンスの永続フィールドをデータベース表および列にマップする例を示しています。
<entity-deployment name="EmpBean" location="emp/EmpBean" wrapper="EmpHome_EntityHomeWrapper2" max-tx-retries="3" table="emp" data-source="jdbc/OracleDS"> <primkey-mapping> <cmp-field-mapping name="empNumber" persistence-name="empnumber" /> </primkey-mapping> <cmp-field-mapping name="empName" persistence-name="ename" /> <cmp-field-mapping name="salary" persistence-name="sal" /> ... </entity-deployment>
デプロイ後、OC4Jは表5-3のように要素値をマップします。
関連フィールドは関連するBeanを特定します。関連フィールドはデータベース表における外部キーと同様に機能します。詳細は「関連フィールド」を参照してください。
関連フィールドは仮想的です。ユーザーはBeanクラスのコードでこれらのフィールドに対するgetterおよびsetterを次のように指定します。
public abstract void setContractInfo(ContractInfo contractInfo) throws RemoteException public abstract ContractInfo getContractInfo() throws RemoteException
コンテナはこれらのメソッドの実装を提供します。
前述した例では、対応する関連フィールドはcontractInfoです。
デプロイメント・ディスクリプタで、このフィールドは<cmr-field><cmr-field-name>要素で次のように定義されます。
<ejb-ralation>
...
<cmr-field>
<cmr-field-name>contractInfo</cmr-field-name>
<cmr-field-type>contractInfo</cmr-field-type>
</cmr-field>
...
</ejb-ralation>
コンテナ管理の関連の詳細は、「コンテナ管理の関連」を参照してください。
各エンティティBeanがデータベースの表にマップされるに従って、その永続および関連フィールドがそれぞれデータベース表内の列に保存されます。これらのフィールドをデータベースにマップするには、次のどちらかを実行します。
これらのフィールドに対するデフォルトを使用します。デプロイメント・ディスクリプタは構成しないでください。ejb-jar.xmlファイルの情報に基づいて、表がBeanに対して自動的に作成されます。詳細は「関連フィールドのデータベースに対するデフォルト・マッピングの構成」を参照してください。
これらのフィールドを、指定されたデータベースにすでに存在する表内の列にマップします。永続データ・マッピングがorion-ejb-jar.xmlファイル内で構成されます。詳細は「関連フィールドのデータベースに対する明示的マッピングの構成」を参照してください。
ejb-jar.xmlファイルで関連フィールドを宣言する場合、OC4Jでは、orion-ejb-jar.xmlファイルの自動生成時に、これらのフィールドのデータベースに対するデフォルト・マッピングが提供されます。関連フィールドに対するデフォルト・マッピングは、永続フィールドに対するマッピングと同じです(「永続フィールドのデータベースに対するデフォルト・マッピングの構成」を参照)。
|
注意: 今後の再デプロイの場合に備えて、指示した表名で自動的に生成されたorion-ejb-jar.xmlファイルを、J2EE_Home/application-deploymentsディレクトリからejb-jar.xmlファイルと同じディレクトリにコピーします。これを実行しないと、それぞれの再デプロイの際に異なる表名が生成される可能性があります。 |
簡単にまとめると、デフォルト・マッピングには次のようなものが含まれます。
データベース: 使用中のOC4Jインスタンス構成に設定されているデフォルトのデータベース。
デフォルト表: 関連における各エンティティBeanは、基底となるデータベース表のデータを表します。エンティティBeanの基底表の名前は一意である必要があるため、名前は次の名前から構成されます。それぞれの名前はアンダースコア文字( _ )で区切られます。
デプロイメント・ディスクリプタの<ejb-name>要素で定義されるEJB名。
.jar拡張子を含むJARファイル名。ただし、SQL表記規則に従ってダッシュ文字( - )およびピリオド( . )はすべてアンダースコア文字( _ )に変換されます。たとえば、JARファイルの名前がaddress.jarの場合は、address_jarが名前に付加されます。
デプロイ時にユーザーが定義したアプリケーション名。
構成された名前が30文字を超えている場合、この名前は24文字で切り捨てられます。アンダースコア文字( _ )、続いて英数字ハッシュ・コードで構成される5文字が名前に付加されて、一意性を確保します。
たとえば、EJB名がAddressEntryで、JARファイル名がaddr.jarで、アプリケーション名がaddressの場合、デフォルトの表名はAddressEntry_addr_jar_addressになります。
各表の列名: コンテナはデプロイメント・ディスクリプタで定義された<cmp-field>および<cmr-field>要素に基づいて、各表の列を生成します。列は、エンティティBeanデータに関連する各<cmp-field>要素に対して作成されます。また、関連を表す各<cmr-field>要素に対しても列が作成されます。単方向の関連(「CMRの方向」を参照)では、関連における一方のエンティティのみがデプロイメント・ディスクリプタで<cmr-field>を定義します。双方向の関連(「CMRの方向」を参照)では、関連における両方のエンティティが<cmr-field>を定義します。
各<cmr-field>要素について、コンテナは関連オブジェクトの主キーを示す外部キーを次のように作成します。
デフォルトの1対1関連では、外部キーはソース・エンティティBeanに対するデータベース表に作成され、ターゲット・データベース表の主キーに方向付けられます。たとえば1人の従業員に1つのアドレスがある場合、アドレス表の主キーを指す外部キーが従業員表の中に作成されます。
1対多関連のデフォルトでは外部キーを使用します。
多対多関連のデフォルトではアソシエーション表(第3表)を作成します。アソシエーション表には2つの外部キーが含まれ、各キーはエンティティ表の1つの主キーを指します。
<cmp-field>および<cmr-field>要素はJavaデータ型を表すため、ユーザーが予想するデータベース型に変換されない場合があります。CMP型をデータベース型に変換するには一定の規則があります(「CMP型のデータベース型への変換」を参照)。Javaデータ型からデータベース・データ型への変換ルールは、J2EE_HOME/config/database-schemasディレクトリにある特定のデータベースXMLファイルで変更できます。このディレクトリにはすべてのデータベース・ファイルが含まれます。Oracleデータベース変換ファイルの名前はoracle.xmlです。
主キー: エンティティBeanの基底表には主キーが含まれます(「主キーの構成」を参照)。主キーのタイプを次に示します。
定義済主キー: 主キーは、単純なデータ型またはクラスとして<primkey-field>要素で指定されたとおりに生成されます。このため、列名は<primkey-field>要素の名前と同じになります。
複合主キー: 主キーはクラス内で定義され、複数のフィールドで構成されます。複合主キー内の各フィールドはデータベース表の列で表されます。ここで各フィールドは表の主キーの一部であると想定されます。
自動的に生成される主キー: <prim-key-class>要素でjava.lang.Objectを主キー・クラス型として指定する場合に、主キー名を<primkey-field>要素で指定しないと、主キーは自動的にコンテナで生成されます。列の名前はAUTOIDです。
<cmp-field>におけるコンテナ管理の永続フィールドと、主キー型を定義する際に、単純なデータ型とシリアライズ可能なJavaクラスを定義できます。
この項の内容は次のとおりです。
表5-4はサポートされている単純なデータ型(persistence-type属性で指定可能)の一覧と、これらの型のSQL型およびOracleデータベース型へのマッピングを示しています。これらのマッピングは、Oracle以外のデータベースでの動作が保証されていない点に注意してください。
表5-4 単純なデータ型
| 既知の型(ネイティブ) | SQL型 | Oracle型 |
|---|---|---|
|
java.lang.String |
VARCHAR(255) |
VARCHAR(255) |
|
java.lang.Integer[] |
INTEGER |
NUMBER(20,0) |
|
java.lang.Long[] |
INTEGER |
NUMBER(20,0) |
|
java.lang.Short[] |
INTEGER |
NUMBER(10,0) |
|
java.lang.Double[] |
DOUBLE PRECISION |
NUMBER(30,0) |
|
java.lang.Float[] |
FLOAT |
NUMBER(20,5) |
|
java.lang.Byte[] |
SMALLINT |
NUMBER(10,0) |
|
java.lang.Character[] |
CHAR |
CHAR(1) |
|
java.lang.Boolean[] |
BIT |
NUMBER(1,0) |
|
java.util.Date |
DATETIME |
DATE |
|
java.sql.Date |
DATE |
DATE |
|
java.uti.Time |
DATE |
DATE |
|
java.sql.Timestamp |
TIMESTAMP |
TIMESTAMP |
|
java.lang.String |
CLOB |
CLOB |
|
char[] |
CLOB |
CLOB |
|
byte[] |
BLOB |
BLOB |
|
java.io.Serializable(最大4KB) |
LONGVARBINARY |
BLOB |
|
注意: これらのデータ型のマッピングはconfig/database-schema/<db>.xml構成ファイルで変更できます。 |
DateとTimeはデータベースのDATEにマップします。これはDATEに時間が含まれるためです。ただし、Timestampは、ナノ秒単位で時間を指定するデータベースのTIMESTAMPにマップします。
java.sql.CLOBおよびjava.sql.BLOBの直接マッピングは、これらのオブジェクトがシリアライズ可能ではないため現在はサポートされていません。ただし、Stringまたはchar[]およびbyte[]を、データベース列型のCLOBおよびBLOBにそれぞれマップできます。char[]のCLOBへのマッピングまたはbyte[]のBLOBへのマッピングは、Oracleデータベースでのみ可能です。Oracle JDBC APIはこの操作を取り扱うように変更されています。
シリアライズ済オブジェクトをシンJDBCドライバでBLOB型にマッピングする際には4KBの制限があります。
Stringおよびchar[]変数をデータベースのVARCHAR2にマップする際は、最大2KBまで保持できます。ただし、2KBを超えるStringオブジェクトまたはchar[]も、次の方法でCLOBにマップできます。
Bean実装でStringまたはchar[]オブジェクトを使用します。
<cmp-field-mapping>要素のpersistence-type属性が次のようにオブジェクトをCLOBとして定義します。
<cmp-field-mapping name="stringdata" persistence-name="stringdata"
persistence-type="CLOB" />
同様に、Bean実装のbyte[]をBLOBに次のようにマップできます。
<cmp-field-mapping name="bytedata" persistence-name="bytedata"
persistence-type="BLOB" />
単純なデータ型の他に、java.io.Serializableインタフェースを実装するユーザー・クラスを定義できます。これらのクラスはデータベースのBLOBに格納されます。
他のエンティティBeanまたはCollectionオブジェクトはCMP型として定義しないでください。これらは関連を示すため、関連フィールド内で次のように定義する必要があります。
別のエンティティBeanとの関連は常に<cmr-field>関連で定義されます。
Collectionオブジェクトは関連の「多」側を形成し、<cmr-field>関連内で構成する必要があります。
|
注意: Collectionのサブインタフェース(Listなど)は使用しないでください。かわりにCollectionを使用してください。 |
「永続フィールドのデータベースに対するデフォルト・マッピングの構成」で説明したように、コンテナは関連フィールドをデータベース表に自動的にマップできます。OC4Jで提供されるデフォルトを使用しない場合、または既存のデータベース表にフィールドをマップする必要がある場合は、既存のデータベース表に対するエンティティBeanとorion-ejb-jar.xmlファイル内のその列との関連を手動でマップできます。
|
注意: 関連フィールドを明示的にマップするように、orion-ejb-jar.xmlファイルの<entity-deployment>要素(表A-1「<entity-deployment>要素の属性」を参照)の各要素および属性を変更する必要があります。JDeveloper IDEにはエンティティBeanとデータベース表の間の複雑なマッピングを管理する機能があります。つまり、JDeveloperはデプロイメント・ディスクリプタを検証して非一貫性を防止します。ユーザーが自分でorion-ejb-jar.xmlファイルを変更することも可能ですが、コンテナ管理の関連の変更にはJDeveloperを使用することをお薦めします。JDeveloperの詳細は「JDeveloperの使用」を参照してください。 |
エンティティBeanのマッピングと既存データベースを手動で照合するには、orion-ejb-jar.xmlファイルを次の手順で変更します。
orion-application.xmlファイルの<autocreate-tables>要素をfalseに設定して、コンテナ管理の永続性を持つエンティティBeanをデプロイします。
application-deployments/ディレクトリからユーザーの開発ディレクトリにorion-ejb-jar.xmlファイルをコピーします。
正しいデータ・ソースを指すように<data-source>要素を変更します。互いに関連付けられているすべてのBeanが同じデータ・ソースを使用する必要がある点に注意してください。
正しい表を指し示すようにtable属性を変更します。これが<entity-deployments>要素で定義されているBeanに対する正しい表であることを確認してください。
各Beanの永続性タイプ(永続フィールドでも関連フィールドでも)に対する正しい列を指すようにpersistence-name属性を変更します。
orion-application.xmlファイルの<autocreate-tables>要素をtrueに設定します。
アプリケーションを再アーカイブしてから再デプロイします。
エンティティBeanのマッピングに照合する既存データベースがない場合に手動でマッピング要素を変更するには、次の手順に従います。
orion-application.xmlファイルの<autocreate-tables>要素をfalseに設定し、ejb-jar.xml要素を構成してBeanをデプロイします。
OC4Jはデフォルト・マッピングが含まれるorion-ejb-jar.xmlファイルを作成します。これらのフィールドは作成するよりも変更する方が簡単です。
J2EE_HOME/application-deploymentsディレクトリからユーザーの開発環境に、コンテナが作成したorion-ejb-jar.xmlファイルをコピーします。
関連タイプに基づいてユーザーが指定したデータベース表および列を使用するように、orion-ejb-jar.xmlファイルの<entity-deployment>要素(表A-1「<entity-deployment>要素の属性」を参照)を変更します。詳細は「データベース表とのBean関連をマップするためのorion-ejb-jar.xmlの構成」を参照してください。
orion-application.xmlファイルの<autocreate-tables>要素をtrueに設定します。
アプリケーションを再アーカイブしてから再デプロイします。
|
注意: <autocreate-tables>をfalseに設定せずにアプリケーションをデプロイすると、OC4Jは自動的にデフォルト表を作成します。これらの表はすべて、アプリケーションを再デプロイする前に削除する必要があります。アソシエーション表がある場合はそれも削除します。 |
エンティティBean間の関連はejb-jar.xmlファイルの<relationships>要素で定義されます。エンティティBeanとデータベース表および列の間のマッピングは、orion-ejb-jar.xmlファイルの<entity-deployment>要素で指定されます。
orion-ejb-jar.xmlファイルは、<cmp-field-mapping>要素内で、データベース表および列とのBeanの関連をマップします。単純な1対1関連についての<entity-deployment>および<cmp-field-mapping>要素のXML構造を次に示します。
<entity-deployment name="SourceBeanName" location="JNDIlocation" table="TableName" data-source="DataSourceJNDIName"> ... <cmp-field-mapping name="CMRfield_name"> <entity-ref home="targetBeanName"> <cmp-field-mapping name="CMRfield_name" persistence-name="targetBean_PKcolumn" /> </entity-ref> </cmp-field-mapping> ...
<cmp-field-mapping>要素内で、Beanの名前(方向を示す関連のソース)、JNDIロケーション、情報を永続させるデータベース表を定義して、ejb-jar.xmlファイルで定義されたそれぞれの永続および関連フィールドを基底データベースにマップします。
<entity-deployment>要素の属性(表A-1「<entity-deployment>要素の属性」を参照)は、Beanについて次の項目を定義します。
name属性は、ejb-jar.xmlファイルの<ejb-name>要素で定義されたBeanのEJB名を指定します。このname属性は、Beanのejb-jar.xmlファイル定義とデータベースへのマッピングとを結び付けます。
location属性はBeanのJNDI名を指定します。
table属性はこのエンティティBeanがマップされる対象のデータベース表を指定します。
data-source属性は表が存在するデータベースを指定します。互いに相互作用するBean、または互いに関連付けられているBeanはすべて、データ・ソースが同じである必要があります。これには、同一アプリケーションのBean、同一トランザクションの一部であるBean、または親子関連にあるBeanが含まれます。
orion-ejb-jar.xmlファイルの<cmp-field-mapping>要素は次のフィールドをデータベース列にマップします。
ejb-jar.xmlファイルの<cmp-field>要素は永続フィールドを定義します。
ejb-jar.xmlファイルの<cmr-field>要素は関連フィールドを定義します。
例5-6 1対1関連におけるejb-jar.xmlとorion-ejb-jar.xmlのマッピング
例5-6は、ejb-jar.xmlファイルの<cmr-field>要素がどのようにorion-ejb-jar.xmlファイルの<cmp-field-mapping>要素にマップされるかを示しています。<cmp-field-mapping>のname属性が2つのXMLファイル間のリンクを提供します。name属性は変更しないでください。
EJB-JAR.XML
<relationship-role-source>
<ejb-name>EmpBean</ejb-name>
</relationship-role-source>
<cmr-field>
<cmr-field-name>address</cmr-field-name>
</cmr-field>
ORION-EJB-JAR.XML
<cmp-field-mapping name="address"> <entity-ref home="AddressBean"> <cmp-field-mapping name="address" persistence-name="addressPK" /> </entity-ref> </cmp-field-mapping>
関連フィールドを完全に指定してマップするには、ネストされた<cmp-field-mapping>要素が使用されます。ネストの書式は関連のタイプによって異なります。ターゲットBeanの主キーであるデータベース列は、内部の<cmp-field-mapping>要素のpersistence-name属性で定義されます。既存のデータベースがある場合、列名を照合するには各<cmp-field-mapping>要素に対するpersistence-name属性を変更します。
仮説モデルでは、1人の従業員(EmpBeanで表される)とそのアドレス(AddressBeanで表される)の間に1対1の単方向の関連(「CMRの方向」を参照)があります。EmpBeanはaddress関連フィールドを使用してAddressBeanを示します。これらの2つのBeanはEMPおよびADDRESSデータベース表にマップされます。EMP表には、ADDRESS表のAddressPKという主キーを示す、addressという外部キーがあります。
例5-7 明示的な1対1の単方向関連マッピング
Beanとその関連はejb-jar.xmlおよびorion-ejb-jar.xmlデプロイメント・ディスクリプタの両方で指定されます。例5-7が示すように、ejb-jar.xmlファイルでは、EmpBeanとAddressBean間の1対1関連が<relationships>要素内で定義されます。方向(「CMRの方向」を参照)は1つまたは2つの<cmr-field>要素で指定されます。
Beanのデータベース永続記憶域へのマッピングはorion-ejb-jar.xmlファイルで定義されます。1対1関連は<cmp-field-mapping>要素内の<entity-ref>要素によって両側にマップされます。<entity-ref>は関連のターゲット・エンティティBeanを示します。
EJB-JAR.XML
<relationships>
...
<ejb-relation>
...
<multiplicity>One</multiplicity>
<relationship-role-source>
<ejb-name>EmpBean</ejb-name>
</relationship-role-source>
<cmr-field>
<cmr-field-name>address</cmr-field-name>
</cmr-field>
...
</ejb-relation>
<ejb-relation>
...
<relationship-role-source>
<ejb-name>AddressBean</ejb-name>
</relationship-role-source>
...
</ejb-relation>
...
ORION-EJB-JAR.XML
<entity-deployment name="EmpBean"...
<cmp-field-mapping name="address">
<entity-ref home="AddressBean">
<cmp-field-mapping name="address" persistentce-name="addressPK" />
</entity-ref>
</cmp-field-mapping>
...
</entity-deployment>
<entity-deployment name="AddressBean"...
...
<cmp-field-mapping name="empNumber">
<entity-ref home="EmpBean">
<cmp-field-mapping name="empNumber" persistentce-name="empnumber" />
</entity-ref>
</cmp-field-mapping>
...
</entity-deployment>
Beanフィールドを既存データベースにマップするには、orion-ejb-jar.xmlファイルの<cmp-field-mapping>要素内のフィールドを理解する必要があります。この要素の構造は次のとおりです。
<cmp-field-mapping name="CMRField_name"> <entity-ref home="targetBeanName"> <cmp-field-mapping name="CMRfield_name" persistence-name="targetBean_PKcolumn" /> </entity-ref> </cmp-field-mapping>
前述した構造の例では次のことが指定されています。
<cmp-field-mapping>要素のname属性は、ejb-jar.xmlファイルの<cmp-field>要素と同じです。<cmp-field-mapping>要素のname属性は変更しないでください。
ターゲットBean名が<entity-ref>要素のhome属性で指定されます。
ターゲットBeanの主キーであるデータベース列は、内部の<cmp-field-mapping>要素のpersistence-name属性で定義されます。既存のデータベースがある場合、列名を照合するには各<cmp-field-mapping>要素に対するpersistence-name属性を変更します。
仮説モデルでは、各従業員(EmpBeanで表される)は1つの部門(DeptBeanで表される)に所属し、各部門には複数の従業員がいる可能性があります。部門表には主キーがあります。従業員表には、各従業員を識別するための主キーと、従業員の部門を示すための外部キーがあります。1人の従業員の部門を探す場合、単純なSQL文で外部キーから部門情報を取り出します。ある部門のすべての従業員を探す場合は、コンテナで部門表および従業員表の両方にJOIN文を実行して、指定した部門番号のすべての従業員を取り出します。
これはデフォルトの動作です。他のデータベース表にマッピングを変更する必要がある場合は、JDeveloperを使用するか、orion-ejb-jar.xmlファイルを手動で変更して<collection-mapping>または<set-mapping>要素を操作します。
|
注意: 関連フィールドを明示的にマップするように、orion-ejb-jar.xmlファイルの<entity-deployment>要素(表A-1「<entity-deployment>要素の属性」を参照)の各要素および属性を変更します。JDeveloperにはエンティティBeanとデータベース表の間の複雑なマッピングを管理する機能があります。JDeveloperはデプロイメント・ディスクリプタを検証して非一貫性を防止します。ユーザーが自分でorion-ejb-jar.xmlファイルを変更することも可能ですが、コンテナ管理の関連の変更にはJDeveloperを使用することをお薦めします。JDeveloperの詳細は「JDeveloperの使用」を参照してください。 |
例5-8 外部キーを使用した、明示的な1対多の双方向関連マッピング
例5-8は、多数の従業員がいる1つの部門の双方向関連におけるマッピングを示しています。関連の「1」側は部門で、関連の「多」側は従業員です。例5-8は、この関連で外部キーを使用するための、手動によるorion-ejb-jar.xmlファイルの変更を示しています。
EJB-JAR.XML
<relationships>
<ejb-relation>
<ejb-relation-name>Dept-Emps</ejb-relation-name>
<ejb-relationship-role>
<ejb-relationship-role-name>Dept-has-Emps</ejb-relationship-role-name>
<multiplicity>One</multiplicity>
<relationship-role-source>
<ejb-name>DeptBean</ejb-name>
</relationship-role-source>
<cmr-field>
<cmr-field-name>employees</cmr-field-name>
<cmr-field-type>java.util.Set</cmr-field-type>
</cmr-field>
</ejb-relationship-role>
<ejb-relationship-role-name>Emps-have-Dept</ejb-relationship-role-name>
<multiplicity>Many</multiplicity>
<cascade-delete/>
<relationship-role-source>
<ejb-name>EmpBean</ejb-name>
</relationship-role-source>
<cmr-field>
<cmr-field-name>dept</cmr-field-name>
</cmr-field>
</ejb-relationship-role>
</ejb-relation>
</relationships>
ORION-EJB-JAR.XML
<enterprise-beans>
<entity-deployment name="DeptBean" data-source="jdbc/scottDS" table="DEPT">
<primkey-mapping>
<cmp-field-mapping name="deptno" persistence-name="DEPTNO" /> /*PK*/
</primkey-mapping>
<cmp-field-mapping name="dname" persistence-name="DNAME" />
<cmp-field-mapping name="employees">
/*points from DEPTNO column in EMP to DEPTNO in DEPT*/
1. <collection-mapping table="EMP"> /*table with FK*/
<primkey-mapping>
<cmp-field-mapping name="DeptBean_deptno"> /*CMR field name*/
<entity-ref home="DeptBean"> /*points to DeptBean*/
2. <cmp-field-mapping name="DeptBean_deptno"
persistence-name="EDEPTNO"/>
</entity-ref>
</cmp-field-mapping>
</primkey-mapping>
<value-mapping type="mypackage1.EmpLocal">
<cmp-field-mapping name="EmpBean_empnumber">
<entity-ref home="EmpBean">
<cmp-field-mapping name="EmpBean_empnumber"
persistence-name="EMPNUMBER"/>
</entity-ref>
</cmp-field-mapping>
</value-mapping>
</collection-mapping>
</cmp-field-mapping>
</entity-deployment>
<entity-deployment name="EmpBean" data-source="jdbc/scottDS" table="EMP">
<primkey-mapping>
<cmp-field-mapping name="empNumber" persistence-name="EMPNUMBER"/>
</primkey-mapping>
<cmp-field-mapping name="empName" persistence-name="ENAME" />
<cmp-field-mapping name="salary" persistence-name="SAL" />
<cmp-field-mapping name="dept"> /*foreign key*/
<entity-ref home="DeptBean">
2. <cmp-field-mapping name="dept" persistence-name="EDEPTNO" />
</entity-ref>
</cmp-field-mapping>
</entity-deployment>
</enterprise-beans>
前述したorion-ejb-jar.xmlの例では、「1」関連(部門)の<collection-mapping>または<set-mapping>要素で識別された表が、ターゲットBeanの表(employee Bean表)の名前である場合、1対多関連は外部キーで定義されます。たとえば、部門定義のtable属性はEMPです。
外部キーは「多」関連のデータベース表で定義されます。前述した例では、EDEPTNO外部キー列はEMPデータベース表に存在します。これはEmpBean構成における<cmp-field-mapping>要素のpersistence-name属性で定義されます。
このため、orion-ejb-jar.xmlファイルの<collection-mapping>または<set-mapping>要素を操作するには、「1」エンティティBean(Collectionを含む)に対する<entity-deployment>要素(表A-1「<entity-deployment>要素の属性」を参照)を次のように変更します。
「1」関連における<collection-mapping>または<set-mapping>表属性の表が、「多」関連のデータベース表になるように変更します。この例では、この属性がEMP表となるように変更します。
「多」関連構成内で「1」関連を示す外部キーを変更します。この例では、persistence-name属性でEDEPTNO外部キーを指定するように<cmp-field-mapping>要素を変更します。
特定のBeanに対してデータベース分離レベル(「エンティティBeanのデータベース分離レベルとリソース競合」を参照)の1つを構成できます。つまり、Beanがトランザクションを開始したときに、このBeanに対するデータベース分離レベルがOC4J固有のデプロイメント・ディスクリプタで(パラレル実行またはデータ整合性において)指定されているレベルになるように指定できます。
|
注意: 一度設定すると、Beanに対する分離レベルはトランザクション全体に対して有効です。 |
<entity-deployment>要素のisolation属性(表A-1「<entity-deployment>要素の属性」を参照)で各エンティティBeanに対する分離レベルを設定できます。committedまたはserializableの値を使用できます。デフォルトはcommittedです。このデフォルト値をserializableに変更するには、orion-ejb-jar.xmlで対象のBeanを次のように構成します。
<entity-deployment ... isolation="serializable" ... </entity-deployment>
serializable分離レベルはデータ整合性を提供し、committed分離レベルはパラレル実行を有効にします。
|
警告: エミュレートされていないデータ・ソースを使用中の場合は、分離レベルを |
|
警告: OC4J固有のデプロイメント・ディスクリプタにおける |
分離レベルを定義しない場合、ユーザーはデータベースで構成されているレベルを受け取ります。OC4J固有のデプロイメント・ディスクリプタ内で分離レベルを設定すると、このBeanに対するグローバル・トランザクションのライフ・サイクル間は、データベースの構成済の分離レベルが一時的にオーバーライドされます。つまり、serializableレベルを使用するようにBeanを定義する場合、トランザクションが終了するまでの間のみ、OC4JではこのBeanに対してデータベースが強制的にserializableとなります。
組合せがリソース競合の結果に影響する場合は、エンティティBeanの並行性モードとデータベース分離レベルの両方を指定することができます。詳細は「エンティティBeanのデータベース分離レベルと並行性モードの組合せ」を参照してください。
リソース競合の詳細は、「データベース・リソースの競合の回避」を参照してください。
並行性モードでは、リソース競合管理のためいつ遮断するか、またはいつパラレル実行を行うかを決定します。詳細は「エンティティBeanの並行性モードとリソース競合」を参照してください。
コンテナ管理の永続性を持つエンティティBeanの並行性モードを設定するには、適切な並行性の値(pessimistic、optimisticまたはread-only)を、OC4J固有デプロイメント・ディスクリプタ(orion-ejb-jar.xmlファイル)の<entity-deployment>要素のlocking-mode属性(表A-1「<entity-deployment>要素の属性」を参照)に追加します。デフォルトの並行性モードはoptimisticです。並行性モードをpessimisticに変更するには、次の変更を実行します。
<entity-deployment ... locking-mode="pessimistic" ... </entity-deployment>
並行性モードはBeanごとに定義され、ロックの効果はトランザクション境界に適用されます。
パラレル実行には、ラッパーおよびBeanインスタンスに対するプール・サイズの正しい設定が必要です。
詳細は次のトピックを参照してください。
<entity-deployment>要素のexclusive-write-access属性(表A-1「<entity-deployment>要素の属性」を参照)は、これがデータベース内の表にアクセスする唯一のエンティティBeanであり、リソースの更新に外部メソッドが使用されないことを示しています。これは、このBeanに対して保持されたキャッシュがすべてこのBeanによってのみ使用されることをOC4Jインスタンスに伝えます。基本的には、この属性をtrueに設定すると、これがこのBean内で使用された表を更新する唯一のBeanであることがコンテナで保証されます。このため、Beanに対して保持されたキャッシュを、バックエンド・データベースから定期的に更新する必要はありません。
このフラグによって、ユーザーによる表の更新が妨げられることはありません。つまり、実際には表はロックされません。ただし、表を手動で更新した場合、または別のBeanから更新した場合、その結果はこのBean内で自動的に更新されません。
exclusive-write-access属性のデフォルト値はfalseです。エンティティBeanの並行性モードの効果(「並行性モードの構成」を参照)によって、read-onlyエンティティBeanについてのみこの要素をtrueに設定できます。OC4Jでは、pessimisticおよびoptimistic並行性モードについてはこの属性を常にfalseにリセットします。
詳細は次のトピックを参照してください。
エンティティBeanクラス(「エンティティBeanクラスの実装」を参照)で、エンティティBeanのコールバック・メソッド(「コールバック・メソッド」を参照)の次の構成を指定する必要があります。
ホーム・インタフェースで定義済の関連付けられたcreateメソッドに一致するパラメータを、ejbCreateメソッドに指定します(「エンティティBeanホーム・インタフェースの実装」を参照)。例4-3「エンティティBeanクラスの実装」でこのメソッドの実装を参照してください。
ejbCreateメソッドで主キーを設定(「主キーの構成」を参照)します。例4-3「エンティティBeanクラスの実装」でこのメソッドの実装を参照してください。
主キー関連がある場合は、ejbCreateメソッドで定義します。例4-3「エンティティBeanクラスの実装」でこのメソッドの実装を参照してください。
Beanに対するjavax.ejb.EntityContextをsetEntityContextメソッドで定義します(例4-3「エンティティBeanクラスの実装」でこのメソッドの実装を参照)。ユーザーは、Beanの存続中に存在する他のリソースをこのメソッド内に割り当てることもできます。
Beanの関連付けられているエンティティ・コンテキストを、setEntityContextメソッドで設定解除(nullに設定)します。他のリソースがある場合は、これもsetEntityContextメソッドで解放します。例4-3「エンティティBeanクラスの実装」でこのメソッドの実装を参照してください。
他のコールバック・メソッドには、エンティティBeanクラスの空の実装のみが必要です。コンテナはこれらのメソッドに対して完全な実装を提供します。ただし、ユーザーのエンティティBean実装のロジックによっては、次の作業に類似した作業を行う場合があります。
外部キー(「複合主キー内の外部キーの構成」を参照)をejbPostCreateメソッドで定義します。
ejbActivateメソッドでリソースを取得し、ejbPassivateメソッドでそのリソースを解放します。