CMR(Container Management Relationship)

CMR(Container Management Relationship)

前面兩期我們提到entity beanJ2EE架構中是用來塑模business object的元件,它同時也是在business tier保存資料的技術之一,它被用來存取保存在EIS tier(Enterprise Information System)的資料,而目前運用的最為普遍的EIS技術則是關聯式資料庫。在關聯式資料庫中,我們以所謂的E-R model(Entity Relationship)來定義表格之間的關係,而這些關係是利用主鍵(Primary Key)與外鍵(Foreign Key)的宣告來表示。由於entity bean所要存取的關聯式資料庫的表格之間有關係存在,因此EJB必須發展出相應的技術來處理entity bean之間的關係,這就是EJB2.0版後提出的CMR(Container Management Relationship)所要處理的問題。

CMP相同,我們是透過所謂的Abstract Schema Persistence/Abstract Programming Model來定義entity beanCMR,這表示我們不需要撰寫存取資料庫的程式碼,我們只需要分別在部署描述子(Deployment Descriptor)bean class中定義entity bean之間的關係,container的部署工具就會在部署時期會自動產生相應的程式碼。下面我們將以MemberAddress兩個entity bean為例子,實際說明CMR的重要觀念與實作方式。

<enterprise-beans>

<entity>

    <ejb-name>Member</ejb-name>

    <local-home>com.eland.ejb.MemberLocalHome</local-home>

    <local>com.eland..ejb.MemberLocal</local>

    <ejb-class>com.eland.ejb.MemberBean</ejb-class>

    <persistence-type>Container</persistence-type>

    <prim-key-class>java.lang.String</prim-key-class>

    <reentrant>False</reentrant>

    <cmp-version>2.x</cmp-version>

    <abstract-schema-name>Member</abstract-schema-name>

    <cmp-field><field-name>userid</field-name></cmp-field>

    <cmp-field><field-name>username</field-name></cmp-field>

    <cmp-field><field-name>password</field-name></cmp-field>

    <primkey-field>userid</primkey-field>

</entity>

<entity>

    <ejb-name>Address</ejb-name>

    <local-home>com.eland.ejb.AddressLocalHome</local-home>

    <local>com.eland.ejb.AddressLocal</local>

    <ejb-class>com.eland.ejb.AddressBean</ejb-class>

    <persistence-type>Container</persistence-type>

    <prim-key-class>java.lang.String</prim-key-class>

    <reentrant>False</reentrant>

    <cmp-version>2.x</cmp-version>

    <abstract-schema-name>Address</abstract-schema-name>

    <cmp-field><field-name>type</field-name></cmp-field>

    <cmp-field><field-name>city</field-name></cmp-field>

    <cmp-field><field-name>street</field-name></cmp-field>

    <primkey-field>type</primkey-field>

</entity>

</enterprise-beans>

 首先我們要說明定義CMR時必須了解的四個概念,關係(Relationship)、角色(Role)、多數性(Multiplicity)、可視性(Navigability)。下圖顯示了這四個概念的關係:

 

1.       <![endif]> 關係

關係表示entity bean之間的連結。在我們的例子中,關係的名字為Member-Address

2.       角色

每一個關係都有兩個角色,分別代表entity bean連結的兩個方向。角色有其名稱,在我們的例子中,兩個角色分別叫做theMemberaddresses

3.       多數性

意指對一角色而言有多少entity bean能夠參與該關係。兩個entity bean之間能夠存在的關係有一對一、一對多、多對一、多對多等。在我們的例子中,MemberAddress之間具有一對多關係,這表示一個Member bean可以有多個Address bean與其連結。我們通常以多數性來決定角色的名稱,以我們的例子而言,由於MemberAddress具有一對多關係,因此我們將定冠詞the加在Member之前,而以複數型的addresses命名Address

4.       可視性

意指一角色是否能夠透過關係被直接存取。可視性可以是單向的,也可以是雙向的。在我們的例子中,箭頭指向的角色是可視的,而這裡的可視性是單向的。從MemberAddress的方向來看,addresses這個角色是可視的,而從AddressMember的方向來看,theMember這個角色是不可視的。若一個角色是可視的,則其相關的entity bean必須有local interface。一個沒有local interfaceentity bean只能與另一個有local interfaceentity bean建立單向的關係。

 接下來我們要看如何在部署描述子中運用上面提到的概念定義CMR。下面是Member-Address這組單向的一對多關係在部署描述子中的定義方式:

 <relationships>

        <ejb-relation>

        <ejb-relation_name>Member-Address</ejb-relation-name>

        <ejb-relationship-role>

        <ejb-relation-role-name>theMember</ejb-relation-role-name>

        <multiplicity>Many</multiplicity>

        <cascade-delete/>

        <relationship-role-source>

        <ejb-name>Address</ejb-name>

</relationship-role-source>

</ejb-relationship-role>

        <ejb-relationship-role>

        <ejb-relation-role-name>Address</ejb-relation-role-name>

        <multiplicity>One</multiplicity>

        <relationship-role-source>

        <ejb-name>Member</ejb-name>

</relationship-role-source>

<cmr-field>

        <cmr-field-name>addresses</cmr-field-name>

        <cmr-field-type>java.util.Collection</emr-field-type>

</cmr-field>

</ejb-relationship-role>

</ejb-relation>

</relationships>

 各標籤的意義如下:

1.    <relationships>標籤在部署描述子中位於<enterprise-beans><assembly-descriptor>標籤之間,一個部署描述子中只能有一個<relationships>標籤。

2.    <relationships>標籤內可以有多組<ejb-relation>標籤,分別定義不同的CMR。每一組<ejb-relation>標籤內可以有一組<ejb-relation-name>標籤用來定義關係名稱。每一組<ejb-relation>標籤內必須有兩組<ejb-relationship-role>標籤用來定義該關係的兩個角色。

3.    <ejb-relationship-role>標籤內的<ejb-relation-role-name>標籤用來定義角色名稱。

4.    <ejb-relationship-role>標籤內的<relationship-role-source>標籤定義了該角色所參照(reference)entity bean的名稱。在我們的例子中,theMember這個角色所參照的entity beanAddress

5.    <relationship-role-source>標籤內的<ejb-name>標籤必須存在於同一個部署描述子的<enterprise-beans>標籤中,亦即一CMR的兩個角色所參照的entity bean必須在同一個EJB模組中。

6.     <ejb-relationship-role>標籤內的<multiplicity>標籤用來定義該角色所參照的entity bean是一個(One)或多個(Many)

7.     如果一個角色是可視的,則必須定義<cmr-field>標籤CMR<cmr-field>標籤內的<cmr-field-name>標籤用來定義CMR的名稱,CMR的名稱必須以小寫開頭,且依慣例與角色名稱相同。每一個<cmr-field-name>在其所參照的bean class裡都必須有抽象的getter/setter方法與其對應,EJB規格書中將這些方法叫做CMR fields

8.     由於theMemberaddresses具有一對多的關係,因此addresses的型態必須是其local interface的集合,我們必須用<cmr-field-type>標籤定義其型別為java..util.Collectionjava.util.Set(不允許重覆)

9.      theMember這個角色中的<cascade-delete>標籤意謂著若刪除一個Member bean,則其所參照的Address bean也會被刪除,如此可以避免在資料庫裡遺留不會再被使用到的資料。這個標籤可以用於一對一與一對多關係,但不能用於多對一與多對多關係。

以上是一對多關係在部署描述子中的定義方式。若關係為一對一,則兩個角色的<multiplicity>標籤都必須定義為One,且都不需要定義<cmr-field-type>標籤;若關係為多對多,則兩個角色的<multiplicity>標籤都必須定義為Many,且兩者的<cmr-field-type>標籤型態都必須定義為java.util.Collectionjava.util.Set。多對一關係同一對多關係。

前面提到,部署描述子中的<cmr-field>標籤必須在bean class中有CMR fields與其對應。宣告CMR fields時必須遵循下列事項:

1. CMR fields的命名格式為get<cmr-field-name>()set<cmr-field-name><cmr-field-name>的值的第一個字母改為大寫,例如getAddress()setAddress()

2.  get<cmr-field-name>()方法的回傳型態必須與傳入set<cmr-field-name>()方法的參數型態一致。型態必須是其所參照的entity beanlocal interfacelocal interface的集合型態(java.util.Collectionjava.util.Set)

值得注意的是,使用CMR fields並非如同使用CMP fields一樣實際存取資料庫裡的資料,而是參照到其他entity bean(setter方法)或取得所參照的entity beanlocal interface reference(getter方法)。因此呼叫一個CMP beanCMR fields,並不會改變資料庫裡的資料,而是改變兩種具有CMR關係的entity bean之間的參照狀態。接下來我們要看一些實際使用CMR fields的例子:

public abstract class MemberBean implements javax.ejb.EntityBean {

        // CMR fields

public abstract java.util.Collection getAddress();

public abstract void setAddress(java.util.Colletion address);

// Business methods

public void addAddress(String type, String city, Sring street) {

InitialContext ctx = new InitialContaxt();

AddressHomeLocal addressHome = ctx.lookup(“AddressHomeLocal”);

AddressLocal address = addressHome.create(type, city, street);

Collection addressHomes = this.getAddress();

addressHomes.add(address);

}

public void updateAddress(String type, String city, String street) {

        Collection addressHomes = this.getAddress();

Iterator iter = addressHomes.iterator();

while(iter.hasNext()) {

        AddressLocal address = (AddressLocal)iter.next();

        if(address.getType.equals(type)) {

                address.setCity(city);

                address.setStreet(street);

                break;

}

}

}

public void removeAddress(String type) {

        Collection addressHomes = this.getAddress();

        Iterator iter = addressHomes.iterator();

while(iter.hasNext()) {

        AddressLocal address = (AddressLocal)iter.next();

        if(address.getType.equals(type)) {

                iter.remove(addess);

                break;

}

}

}

}

1.  由於MemberAddress具有一對多關係,且AddressMember而言具有可視性,因此我們在Memebr bean class中宣告一組CMR fields,該CMR fieldsjava.util.Collection為其回傳型態與參數型態。

2.   在上面的範例中,我們宣告了三個使用getAddress() CMR fieldbusiness methodsaddAddress()方法新增一個Address bean,並建立該entity beanMember bean之間的關係,意即在使用getAddress() CMR field所取回的集合中新增一個Address beanlocal interface reference,而updateAddress()方法與removeAddress()方法則在使用getAddress() CMR field取回的集合中找到合乎條件的Address beanlocal interface reference,然後修改或刪除。需要注意的是iter.remove(address)這行程式碼並沒有真的刪除address這個local interface reference所代表的entity bean,被刪除的是entity beanentity bean之間的參照,而不是entity bean或資料庫裡的資料。

3.  使用setAddress() CMR field要特別注意,因為它意謂著清除原來集合中所有local interface references,另以新的local interface references取代。舉例而言,如果要將兩個不同的Member bean所參照的Address bean對換,我們可以以下面的例子為之:

Collection addressHomesA = memberA.getAddress();

memberB.setAddress(addressHomesA);

但如果只是要將原先為memberA所參照到的一個Address beanmemberA移到memberB,那我們必須先取得該Address beanlocal interface reference後,再將其加入memberB所參照的Address beanlocal interface references的集合,示範如下:

Collection addressHomesA = memberA.getAddress();

Collection addressHomesB = memberB.getAddress();

Iterator iter = addressHomesA.iterator();

while(iter.hasNext()) {

AddressLocal addressA = (AddressLocal)iter.next();

if(// Condition) {

        addressHomesB.add(addressA);

        break;

}

}

4.   除了上面的business methods外,我們也可以在新增一個entity bean時,在bean classcreate方法中使用CMR fields建立或變更與其他entity bean的參照。依照EJB規格書規定,這必須在ejbPostCreate()方法中為之。

CMR的介紹到此告一段落,下期我們將介紹與CMPCMR一樣使用Abstract Schema Persistence/Abstract Programming Model來實作的Query LanguageQuery Methods

參考資料

1.  Richard Monson-Haefel ---- Enterprise JavaBeans, 3rd Edition, O’Reilly, 2001

2.  Ueli Wahli, Wouter Denayer, Lars Schunk, Deborah Shaddon, Martin Weiss ---- EJB 2.0 Development with WebSphere Studio Application Developer, 1st Edition, IBM/Redbooks, 2003

你可能感兴趣的:(CMR(Container Management Relationship))