在這篇文章中,我們將討論:
1.CMP 2.0: 發生了什麼變化?
2.Inventory(貨物清單)EJB應用程序
3.開發一個CMP bean
4.移植CMP bean到BMP
CMP 2.0: 發生了什麼變化?
當EJB問世的時候,CMP引起了很大的反響,當EJB2.0問世的時候,又對CMP模型進行了修訂,它給了我們真正想要的特性:諸如關係和查詢語言標準化。
與EJB1.1相比,當我們用EJB2.0寫一個CMP bean時,我們的寫法有很大的不同。我們創建抽象類,而不是創建被容器管理變量的公共域,我們像JavaBean一樣創建抽象的屬性(getters and setters)。這准許特定廠商的持久性管理器用他們自己的方式實現數據訪問器(accessors)。
這將幫助他們(廠商)提出像這樣的邏輯:
●由於他們不調用任何set方法,所以在ejbStore()中不做任何動作
●他們僅僅改變一個域,所以我們只在UPDATE查詢語句中set那個域
●我們延遲裝載一些數據,所以當用戶用GET方法請求數據的時候我們纔讀取他
順便提一個問題:爲什麼我們必須在抽象類中創建抽象方法?爲什麼持久性管理器不能在派生類中創建方法?
答:我們必須在抽象類中訪問這些方法。例如:在ejbCreate()中,我們通過傳入參數來SET它們。
Inventory EJB 應用程序
爲了說明該實體模型,我們將看到一個簡單的應用用該實體模型化該貨物清單(Inventory)系統。我們應用程序包括以下組成部分:
Inventory實體Bean: 這是我們的焦點。他將映射到數據庫表Inventory,該表紀錄了條目的名字(主關鍵字),價格和倉庫中條目的數目
價格無狀態會話Bean:該Bean用Inventory實體Bean來得到條目的價格,它用本地(local)接口來訪問該實體
價格客戶: 這個命令行應用程序在會話(session)上運行該方法來測試所有工作都運行得很好
關鍵點是當我們把這個Inventory實體從 CMP移植到BMP實現時候,什麼也不要做改變
讓我們看一下用CMP實現Inventory實體
開發一個CMP bean
inventory CMP要我們創建一個抽象類(依照實體說明),XML描述文件告訴框架應該影射什麼,圖一說明了我們的條目看起來是什麼樣子
抽象實體bean
該抽象實體有下列屬性:
實現了javax.ejb.EntityBean接口
abstract public class InventoryBean implements EntityBean |
實現在實體接口中聲明的方法
public void setEntityContext(EntityContext context) { ctx = context; } public void unsetEntityContext() { ctx = null; } public void ejbActivate() {} public void ejbPassivate() {} public void ejbRemove() throws RemoveException {} public void ejbStore() {} public void ejbLoad() {} |
實現ejbCreate()和ejbPostCreate()方法該方法對應於HOME接口中的Create()方法(用抽象set方法set所有參數)
public String ejbCreate(String item, float price, int stock) throws CreateException { setItem(item); setPrice(price); setStock(stock); return null; } |
public void ejbPostCreate(String item, float price, int stock) throws CreateException {}
實現抽象get和set方法
public abstract String getItem(); public abstract void setItem(String item); public abstract float getPrice(); public abstract void setPrice(float price); public abstract int getStock(); public abstract void setStock(int stock); |
實現一個助手方法來訪問實體上下文(在後面的BMPbean中我們將用到它)
public EntityContext getEntityContext() { return ctx; } |
EJB部署描述符
我們創建了一個CMP實體類,現在該創建部署描述符了.首先我們將創建一個標準的"ejb-jar.xml",然後我們需要配置與廠商有關的信息.我們將說明WebLogic 6.1 部署描述符,我們選一個.
標準 ejb-jar.xml
基本的實體配置信息:
我們將配置類名,用局部變量,在實體內部它們是局部變量.我們通過持久類型XML標籤告訴容器該實體是CMP
<entity>
<ejb-name>InventoryBean</ejb-name>
<local-home>InventoryHome</local-home>
<local>Inventory>/local<
<ejb-class>InventoryBean</ejb-class>
<persistence-type>Container</persistence-type>
<prim-key-class>java.lang.String</prim-key-class>
<reentrant>False</reentrant>
接着,我們將告訴容器那些域是容器管理的,item域是主鍵類:
特定廠商的weblogic-ejb-jar.xml
我們首先在weblogic-ejb-jar.xml中定義廠商特定的信息。在這裏我們告訴服務器在哪兒存放本地存根(stub)和哪兒去查找CMP映射。
持久映射信息:
<persistence-type>
<type-identifier>WebLogic_CMP_RDBMS</type-identifier>
<type-version>6.0</type-version>
<type-storage>META-INF/weblogic-cmp-rdbms-jar.xml</type-storage>
</persistence-type>
本地JNDI名字
<local-jndi-name>InventoryHome</local-jndi-name>
特定廠商的weblogic-cmp-rdbms-jar.xml
數據源(datasource)名稱:
(這個數據源在配置的時候被創建)
<data-source-name>InventoryDB</data-source-name>
表(table)名:
<table-name>inventory</table-name>
域(field)映射
<field-map>
<cmp-field>stock</cmp-field>
<dbms-column>stock</dbms-column>
</field-map>
<field-map>
<cmp-field>item</cmp-field>
<dbms-column>item</dbms-column>
</field-map>
<field-map>
<cmp-field>price</cmp-field>
<dbms-column>price</dbms-column>
</field-map>
現在我們有了我們想要得一切。容器將利用抽象類和部署信息來使一切工作的很好。由於某些原因,我們可能讓這個CMPbean移植到BMP。以下是爲什麼我們要這樣做的原因
1.可移植性:當前,它只工作在WebLogic 6.1,,而我們並不想學習其他的工具。這非常正常如果我們想賣這個數據庫組件的話--它能夠運行在任何服務器上。
2.性能:如果我們想很好的運行SQL(用存儲過程,表的聯合等等)。
3.可替換的數據源:如果我們想訪問不同的數據源而不是RDBMS。
用BMP移植Bean
這裏我們將討論移植模式。我們可以通過“變成持久管理器“模式將CMP移植到BMP。我們可以看到持久管理器擴展了我們創建的抽象類並實現了抽象類的方法!最終的設計看起來如圖二:
BMP實體bean
讓我們看看這個即將成爲BMPbean的bean類:
擴展抽象CMPbean類:
public class InventoryBeanBMP extends InventoryBean {}
創建映射域:
public String item;
public float price;
public int stock; 改寫ejb*()系列方法:
爲了改寫EJB方法,我們需要記住在BMP中我們對每一個方法所負的責任。
大多數項目看起來是一樣的,所以讓我們看其中一個方法(你可以在所下載的代碼中看到其餘代碼)
public String ejbCreate(String item, float price, int stock) throws CreateException {
// insert row into database
this.item = item;
this.price = price;
this.stock = stock;
// 插入數據庫記錄
try {
Connection connection = getConnection();
PreparedStatement statement = connection.prepareStatement
("INSERT INTO inventory (item, price, stock) valueS (?, ?, ?)");
statement.setString(1, item);
statement.setFloat(2, price);
statement.setInt(3, stock);
if (statement.executeUpdate() != 1) {
statement.close();
connection.close();
throw new CreateException("Could not create: " + item);
}
statement.close();
connection.close();
return item;
}
catch(SQLException e) {
throw new EJBException("Could not create: " + item, e);
}
}
實現get 和set方法:
public String getItem() {
return this.item;
}
public void setItem(String item) {
this.item = item;
}
public float getPrice() {
return this.price;
}
public void setPrice(float price) {
this.price = price;
}
public int getStock() {
return this.stock;
}
public void setStock(int stock) {
this.stock = stock;
}
輔助函數得到一個JDBC連接:
private Connection getConnection() throws SQLException {
DataSource ds = null;
try {
Context ctx = new InitialContext();
ds = (DataSource) ctx.lookup ("java:comp/env/jdbc/InventoryDB") ;
} catch (NamingException exp) {
exp.printStackTrace() ;
}
return (ds == null) ? null : ds.getConnection();
}
EJB 部署描述符:
標準ejb-jar.xml
從CMP轉換成BMP時要改變部署環境:
class指向BMP類而不是抽象類,我們要讓容器知道我們自己管理持久:
<ejb-class>InventoryBeanBMP</ejb-class>
<persistence-type>Bean</persistence-type> 加入JDBC數據源引用:(在上面getConnection()方法中使用)
<resource-ref>
<res-ref-name>jdbc/InventoryDB</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref> 廠商特定的 weblogic-ejb-jar.xml:
<reference-descriptor>
<resource-description>
<res-ref-name>jdbc/InventoryDB</res-ref-name>
<jndi-name>inventoryDB</jndi-name>
</resource-description>
</reference-descriptor>
我們不必過多地關心廠商特定的文件,正如我們在CMP中所做的。我們需要指定本地JNDI名稱(與以前一樣),我們需要指出數據源的本地地址,它部署在應用服務器中。我們已經重用了抽象類,改寫它滿足我們所需要得BMP實現,然後把我們的部署描述符打包
結論:
我們已經研究了能幹淨利落讓CMP轉換爲BMP實現的模式。我們的BMP實現只是簡單的擴展了CMP抽象bean類然後讓它運行!注意到我們以相同的邏輯加入了get/set方法以確保我們在ejbStore()中不做任何動作,或者做一個查詢。我們仍就要記住儘可能的用CMPbean,BMP做爲最後的手段。
部署例程注意事項:
你可以下載例程模式的代碼。代碼有兩部分 BMP 和 CMP(它們有相同的目錄),我們提供了一個例子創建腳本(build.cmd)來編譯和部署該例程到BEA WebLogic 6.1。我們需要在腳本中改變一些目錄路徑讓它運行。爲了測試該應用可以在客戶端運行命令:javac PricerClient。請仔細參閱README.txt 文件來得到更多的信息(如建立一個數據庫表等)。
Dion Almaer是中間件公司主要技術人員和TheServerSide.Com J2EE 社區的首席架構師.