一:
第一個設計模式非常簡單。一個公司和僱員的Entity Bean和
下面給出的Entity Bean的代碼片斷是類似的。它們是由jbuilder4的
EntityBean模版生成的。所有的字段都聲明爲public的cmp字段。
Code snippet for Company Entity Bean |
//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 |
現在,公司和僱員的entity bean可以把上面的一個結構類型作爲
ejbCreate()的一個參數。由於這個結構封裝了entity的所有字段
的值,entity bean只需要一個getdata()和setdata()方法就可以
對所有的字段進行操作。
Code snippet for an Entity Bean’s create() |
跟設計模式1中使用單獨的get()和set()方法去操作特定字段不同,
在設計模式2中,我們避免這種情況而只需要進行一次遠程調用就
可以了。現在,只有一個事務通過一次遠程調用就操作了所有的數
據。這樣,我們就避免了設計模式1的大部分缺點,除了建立bean
之間的關係外。
雖然setdata()方法可以對所有字段賦值,但是,borland appserver
提供了一種智能更新的特性,只有被修改過的字段纔會被重新寫入數
據庫,如果沒有字段被修改,那麼ejbStore()方法將會被跳過。
borland程序員開發指南(EJB)有更詳細的描述。
同樣,在entity bean和struct之間存在這重複的代碼,比如同
樣的字段聲明。這意味着任何數據庫表結構的修改都會導致
entity beabn和struct的改變,這使得同步entity和struct變得
困難起來。
就是在ebCreate()方法中調用setddata()方法,這可以消除一
些冗餘的代碼。
Code snippet for an Entity Bean’s create() |
三:
在設計模式2中我們看到,在entity bean和struct之間
有很多重複的代碼比如同樣的字段聲明(對應數據庫中的表列)。
如果讓entity bean從結構繼承下來就可以避免冗餘的代碼。但是
這種設計,仍然不能顯示beans之間的聯繫。
Code snippet for Company Entity Bean |
其餘的代碼比如getdata()和setdata()方法的實現和設計模式2中
是完全一樣的。
四:
在設計模式3中我們看到使bean從struct繼承後使得代碼大
幅縮水並且所有的字段都可定義爲cmp字段。這裏,我們可
以更進一步修正setdata()和getdata()的實現方法來減少代碼量。
我們爲這個struct增加一個方法。
value Object code snippet for Company |
由於entity bean是從struct繼承下來的,在bean的實現類
中也一樣可以引用copyfrom()方法,當然,必須注意的是,
這個copyfrom()方法並不是一個商業方法,它不需要在bean
的遠程接口中暴露給調用者。
現在,getdata()和setdata()方法可以簡化更進一步的簡化。
Code snippet for an Entity Bean’s getData() |
這裏把this作爲一個參數傳入copyfrom()。由於enttity bean
從struct繼承而來,於是這個entitty bean便可以作爲一個
struct傳入。
EJB容器並不贊成把this指針作爲一個參數傳遞因爲在兩個控
制線程中同時訪問一個bean的實例可能會引起事務衝突。但事
實上我們所做的並沒有違背這個原則,因爲我們的並沒有在
bean之間傳遞this的引用並且也沒有引用任何可能引起事務衝突的方法。
Code snippet for an Entity Bean’s setData() |
對於一個映射到有很多列的表的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 |
下面是僱員Entity Bean的程序片段:
public class EmployeeBean extends EmployeeStruct |
在上面的程序片段中,僱員Entity Bean從僱員結構繼承,僱員結構本身有
一個字段comId表示僱員和公司之間的的外鍵,在所有的前面的設計模式中,
這個字段是CMP的.而在設計模式5中這個字段用在Deployment Descriptor中un-checking的方法從CMP中去掉.而對公司Entity Bean的遠程引用現在是CMP的.現在的問題是怎麼在getData()和SetData()方法中更新公司Entity Bean的引用,當這些方法只get和set comId(在設計模式上下文中沒有被CMP)的值.簡單的說,過程的結構沒有變化並且字段comId(不再CMP)在RPC中被拷貝到Entity Bean和從Entity Bean拷貝出來.需要的是對公司Entity Bean的遠程引用在必須被寫入數據庫和從數據庫讀出時更新.我們需要用ejbLoad()和ejbStore()方法在Entity Bean實現類中爲我們完成這項工作.
在僱員Entity Bean中的ejbLoad()方法的代碼片段如下:
|
以上代碼幾乎不需要解釋.當數據被從數據庫中讀出(在事務的開始時候),
comId(不是CMP)字段在僱員Entity Bean被set.因此當getData()方法被調用時,返回的結構將包含正確地comId的值.
在僱員Entity Bean中的ejbStore()方法如下:
public void ejbStore() { |
ejbStore()在事務結束當數據被寫入數據庫時被調用.在這種情況下,comId的值被修改(通過調用setData方法),this必須被寫到數據庫中.在上面方法中的代碼把comId轉化成公司的遠程引用.(畢竟comId是公司Entity Bean的主鍵).
使用空check的原因是數據庫不能存空值(表之間的弱引用),並且這些同樣需要建模.
任何情況下,用java對基本類型的封裝要比使用基本類型自己好,因爲他們能
存空值而且易於轉換成其他形式.
上面的BeanGlossary類的代碼片斷容易引起一些混淆.
這實際上是一個捕獲EJB的lookup的utility類(一個無狀態session bean),
在entity bean和有狀態session bean的情況下,Home接口的lookup是被緩衝的.在無狀態session bean的情況下,Remote接口是被緩衝的(作爲ejb規範1.1的一部分,一個SLSB在Home接口中調用的create()是不被優化的).
通過在上面上下文的緩衝,我們意思是第一個請求是被lookup的.隨後的調用是得到已經在對象引用中初始化的home接口或remote接口.
BeanGlossarySB utility類的代碼片段如下:
public class BeanGlossarySB implements SessionBean { |
在設計模式5中,我們沒有處理Entity Bean的Home接口.
在僱員Entity Bean的情況下, 會有一個finder元素在
findEmployeesByCompany(Company pCompany)的幾行中,
這將會返回僱員遠程引用的集合. 在公司Entity Bean 中的Deployment
Descriptor map了在上面定義的finder元素的僱員集合.
這樣,在公司Entity Bean中的方法getEmployees()在remote接口中的調用
返回需要的與那家公司相聯繫的遠程引用的僱員的集合.