EJB設計模式

一:

第一個設計模式非常簡單。一個公司和僱員的Entity Bean
下面給出的Entity Bean的代碼片斷是類似的。它們是由jbuilder4
EntityBean
模版生成的。所有的字段都聲明爲publiccmp字段。

Code snippet for Company Entity Bean
public class CompanyBean implements EntityBean {
EntityContext entityContext;
public Integer comId; //the primary key
public String comName; //the company name
public String comDescription //basic description
public Timestamp mutationDate //explained later
public Integer ejbCreate(<params>) throws
CreateException {
return null;
}
//various get() and set() for every column/field
// which are exposed in the Remote Interface as well
Code snippet for Employee Entity Bean
public class EmployeeBean implements EntityBean {


EntityContext entityContext;
public Integer empId; //the primary key
public Integer comId; //the company foreign key
public String empFirstName; //the employee firstname
public String empLastName // the employee lastname
public Timestamp mutationDate //explained later
public Integer ejbCreate(<params>) throws
CreateException {
return null;
}



//various get() and set() for every column/field
// which are exposed in the Remote Interface as well
這個設計模式雖然很簡單,但是卻有很多缺點,比如,對每一個
字段的訪問都會導致對get()set()方法的一次遠程調用。而遠
程過程調用(RPCs)是非常耗費資源的,並且,對於在實際中通
常要求的組合的訪問會導致一系列的遠程調用。可以說,這個模
式在實際中可用性很差。上面展示的設計模式可以作爲其他設計
模式的基礎,比如RAD,原型設計,測試等。這時,那個代表僱
員的Employee Entity Bean並沒有展示出在僱員和公司之間有何
關係。
 
二:

爲了避免設計模式1的缺點,我們介紹一下封裝
entity bean
值域的value objec的概念。value object
用某些語言的術語來說,就是一個結構類型,因爲他們
corba的結構類型非常類似。

value Object code snippet for Company
public class CompanyStruct implements
java.io.Serializable {
public Integer comId; //Primary Key
public String comName;
public String comDescription;
public java.sql.Timestamp mutationDate;
}
value Object code snippet for Employee
public class EmployeeStruct implements
java.io.Serializable {
public Integer empId; //Primary Key
public Integer comId; //Foreign Key
public String empFirstName;


public String empLastName;
public java.sql.Timestamp mutationDate;
}




現在,公司和僱員的entity bean可以把上面的一個結構類型作爲
ejbCreate()
的一個參數。由於這個結構封裝了entity的所有字段
的值,entity bean只需要一個getdata()setdata()方法就可以
對所有的字段進行操作。

Code snippet for an Entity Bean’s create()
public Integer ejbCreate(CompanyStruct struct) throws
CreateException {
this.comId = struct.comId;
this.comName = struct.comName;
this.comDescription = struct.comDescription;
this.mutationDate = struct.mutationDate;
return null;
}
Code snippet for an Entity Bean’s getData()
public CompanyStruct getData() {
CompanyStruct result = new CompanyStruct();
result.comId = this.comId;
result.comName = this.comName;
result.comDescription = this.comDescription;


result.mutationDate = this.mutationDate;
return result;
}
Code snippet for an Entity Bean’s setData()
public void setData(CompanyStruct struct) {
this.comName = struct.comName;
this.comDescription = struct.comDescription;
this.mutationDate = struct.mutationDate;;
}



跟設計模式1中使用單獨的get()set()方法去操作特定字段不同,
在設計模式2中,我們避免這種情況而只需要進行一次遠程調用就
可以了。現在,只有一個事務通過一次遠程調用就操作了所有的數
據。這樣,我們就避免了設計模式1的大部分缺點,除了建立bean
之間的關係外。
雖然setdata()方法可以對所有字段賦值,但是,borland appserver
提供了一種智能更新的特性,只有被修改過的字段纔會被重新寫入數
據庫,如果沒有字段被修改,那麼ejbStore()方法將會被跳過。
borland程序員開發指南(EJB)有更詳細的描述。
同樣,在entity beanstruct之間存在這重複的代碼,比如同
樣的字段聲明。這意味着任何數據庫表結構的修改都會導致
entity beabn
struct的改變,這使得同步entitystruct變得
困難起來。


就是在ebCreate()方法中調用setddata()方法,這可以消除一
些冗餘的代碼。

Code snippet for an Entity Bean’s create()
public Integer ejbCreate(CompanyStruct struct) throws
CreateException {
this.comId = struct.comId; //set the primary key
setData(struct);//this removes some redundant code
return null;
}
 


三:

在設計模式2中我們看到,在entity beanstruct之間
有很多重複的代碼比如同樣的字段聲明(對應數據庫中的表列)。
如果讓entity bean從結構繼承下來就可以避免冗餘的代碼。但是
這種設計,仍然不能顯示beans之間的聯繫。

Code snippet for Company Entity Bean
public class CompanyBean extends CompanyStruct
implements EntityBean {
EntityContext entityContext;
//all fields in CompanyStruct are available for CMP
public Integer ejbCreate(CompanyStruct Struct)
throws CreateException {
this.comId = struct.comId; //set the primary key
setData(struct);//this removes some redundant code
return null;
}


其餘的代碼比如getdata()setdata()方法的實現和設計模式2
是完全一樣的。


四:

在設計模式3中我們看到使beanstruct繼承後使得代碼大
幅縮水並且所有的字段都可定義爲cmp字段。這裏,我們可
以更進一步修正setdata()getdata()的實現方法來減少代碼量。
我們爲這個struct增加一個方法。

value Object code snippet for Company
public class CompanyStruct implements
java.io.Serializable {
public Integer comId;
public String comName;
public String comDescription;
public Timestamp mutationDate;
public void copyFrom(CompanyStruct struct) {
comId = struct.comId;
comName = struct.comName;
comDescription = struct.comDescription;
mutationDate = struct.mutationDate;
}
}




由於entity bean是從struct繼承下來的,在bean的實現類
中也一樣可以引用copyfrom()方法,當然,必須注意的是,
這個copyfrom()方法並不是一個商業方法,它不需要在bean
的遠程接口中暴露給調用者。
現在,getdata()setdata()方法可以簡化更進一步的簡化。

Code snippet for an Entity Bean’s getData()
public CompanyStruct getData() {
CompanyStruct result = new CompanyStruct();
result.copyFrom(this);
return result;
}



這裏把this作爲一個參數傳入copyfrom()。由於enttity bean
struct繼承而來,於是這個entitty bean便可以作爲一個
struct
傳入。
EJB
容器並不贊成把this指針作爲一個參數傳遞因爲在兩個控
制線程中同時訪問一個bean的實例可能會引起事務衝突。但事
實上我們所做的並沒有違背這個原則,因爲我們的並沒有在
bean
之間傳遞this的引用並且也沒有引用任何可能引起事務衝突的方法。

Code snippet for an Entity Bean’s setData()
public void setData(CompanyStruct struct) {
this.copyFrom(struct);
}





對於一個映射到有很多列的表的entity bean,這種實現
方法的優點是使得bean實現類的代碼非常簡單。這種設
計模式使得代碼及其精簡,可讀性和可維護性也大大增強。
任何數據庫的修改都只需要修改作爲基類的struct,而幾
乎不需要修改bean的代碼。把這種改變從struct分離出來,
cmp字段發生改變時需要修改部署描述符。這就使得開
發時能夠更好的適應設計的改變。
這裏,還是沒有實現bean之間的關係,這將在設計模式5中解決。
 
五:

就像我們在設計模式4中看到的, Entity Bean的實現大小被縮減到在ejbCreate(), getData()and setData()方法中的僅僅幾行,不管CMP字段的數目.下一步是建模公司和僱員的Entity Beans,這個有點繁瑣而且建議讀者先對borland公司的<EJB程序員指南>的OR Mapping和高級CMP有所瞭解.
對這個關係建模根本不需要對結構的代碼變化,然而Entity Beans實現類需要一點點修改來反映兩個實體間的關係,鑑於此Deployment Descriptor需要有小的修改.

象以前, Entity Bean從結構繼承,下面是公司Entity Bean的代碼片段:

public class CompanyBean extends CompanyStruct
implements EntityBean {
EntityContext entityContext;
// CMP for all fields in the CompanyStruct
public java.util.Collection employees; //one-to-many
//rest of the code including getData() and setData()
public java.util.Collection getEmployees() {
return employees;
}
}





下面是僱員Entity Bean的程序片段:

public class EmployeeBean extends EmployeeStruct
implements EntityBean {
EntityContext entityContext;
//CMP for all fields in EmployeeStruct EXCEPT
//the comId
public Company company;//remote reference to company
}




在上面的程序片段中,僱員Entity Bean從僱員結構繼承,僱員結構本身有
一個字段comId表示僱員和公司之間的的外鍵,在所有的前面的設計模式中,
這個字段是CMP.而在設計模式5中這個字段用在Deployment Descriptorun-checking的方法從CMP中去掉.而對公司Entity Bean的遠程引用現在是CMP.現在的問題是怎麼在getData()SetData()方法中更新公司Entity Bean的引用,當這些方法只getset comId(在設計模式上下文中沒有被CMP)的值.簡單的說,過程的結構沒有變化並且字段comId(不再CMP)RPC中被拷貝到Entity Bean和從Entity Bean拷貝出來.需要的是對公司Entity Bean的遠程引用在必須被寫入數據庫和從數據庫讀出時更新.我們需要用ejbLoad()ejbStore()方法在Entity Bean實現類中爲我們完成這項工作.
在僱員Entity Bean中的ejbLoad()方法的代碼片段如下:


public void ejbLoad() {
try {
comId=(company ==


null)?null:(Integer)company.getPrimaryKey();
} catch (Exception e) {
//throw some runtime exception (e.g. EJBException)
}
}



以上代碼幾乎不需要解釋.當數據被從數據庫中讀出(在事務的開始時候),
comId(
不是CMP)字段在僱員Entity Beanset.因此當getData()方法被調用時,返回的結構將包含正確地comId的值.
在僱員Entity Bean中的ejbStore()方法如下:

public void ejbStore() {
try {
company = (comId ==
null)?null:beanGlossary.getCompanyHome().findByPrimary
Key(comId);
} catch (Exception e) {
//throw some runtime exception (e.g. EJBException)
}
}



ejbStore()
在事務結束當數據被寫入數據庫時被調用.在這種情況下,comId的值被修改(通過調用setData方法),this必須被寫到數據庫中.在上面方法中的代碼把comId轉化成公司的遠程引用.(畢竟comId是公司Entity Bean的主鍵).
使用空check的原因是數據庫不能存空值(表之間的弱引用),並且這些同樣需要建模.


任何情況下,java對基本類型的封裝要比使用基本類型自己好,因爲他們能
存空值而且易於轉換成其他形式.
上面的BeanGlossary類的代碼片斷容易引起一些混淆.
這實際上是一個捕獲EJBlookuputility(一個無狀態session bean),
entity bean和有狀態session bean的情況下,Home接口的lookup是被緩衝的.在無狀態session bean的情況下,Remote接口是被緩衝的(作爲ejb規範1.1的一部分,一個SLSBHome接口中調用的create()是不被優化的).
通過在上面上下文的緩衝,我們意思是第一個請求是被lookup.隨後的調用是得到已經在對象引用中初始化的home接口或remote接口.
BeanGlossarySB utility
類的代碼片段如下:

public class BeanGlossarySB implements SessionBean {
private Context context = null;
public javax.naming.Context getContext() throws
NamingException {
if (context == null)
context = new javax.naming.InitialContext();
return context;
}
// Company
private CompanyHome companyHome = null;
public CompanyHome getCompanyHome() throws
NamingException {


companyHome = ((CompanyHome)
javax.rmi.PortableRemoteObject.narrow(
getContext().lookup("java:comp/env/ejb/Company"),
CompanyHome.class));
return companyHome;
}
// rest of the EJBs
}



在設計模式5,我們沒有處理Entity BeanHome接口.
在僱員Entity Bean的情況下會有一個finder元素在
findEmployeesByCompany(Company pCompany)
的幾行中,
這將會返回僱員遠程引用的集合在公司Entity Bean 中的Deployment
Descriptor map
了在上面定義的finder元素的僱員集合.
這樣,在公司Entity Bean中的方法getEmployees()remote接口中的調用
返回需要的與那家公司相聯繫的遠程引用的僱員的集合.
 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章