|
|
內文: |
|
介紹 |
應用程式設計 |
建立Entity bean |
定義 CMP 欄位 |
尋找程式和建立方法 |
建立Session bean |
建立客戶端應用程式 |
編譯和包裝應用程式 |
尋找程式方法和 EJB-QL |
結語 |
參考資料 |
關於作者 |
|
|
|
http://www2.tw.ibm.com/developerWorks/tutorial/content/java/t20031008_ejb_part1.html 介紹 CMP/CMR 的介紹 本技術教學向您介紹 Enterprise JavaBeans 2.0(EJB)中容器管理的持久性(Container-Managed Persistence(CMP))和容器管理的關係(Container-Managed Relationships(CMR))。這些特性是 EJB Entity bean 特有的,與一般短時間存在的Session bean 相比較,Entity bean 通常長時間存在。 透過加入以下這些對Entity bean 的進階支援,Enterprise JavaBeans(EJB)2.0 擴充早期的版本 1.1︰
- 更新 Entity bean 的容器管理持久性(CMP)
- 對容器管理關係(CMR)的支援
- 在配置描述子中定義用於可移植的選擇(select)和尋找(find)方法的 EJB 查詢語言(EJB-Query Language(EJB-QL))
- 加入local interfaces和 local home interfaces來最佳化對同一容器中其它 bean 的存取
如果您想要購買或出售元件,您很可能希望在您的元件中有一個持久層,能夠在應用程式伺服器(例如,IBM WebSphere、BEA、WebLogic、JBoss/Tomcat 等等)和持久性儲存系統(比如,Oracle、DB2 等等)上跨平台工作。您不必在 EJB 中編寫底層的 JDBC呼叫來加入這些功能,這就節省大量時間並降低複雜程度。一旦您掌握 CMP/CMR,使用這種技術編寫Entity bean 比在bean-managed persistence(BMP)的 bean 內部使用 JDBC 進行編寫快。 應該關心 CMP/CMR 嗎? 這對您來說意味著什麼呢?對於初學者,您不必編寫低級的 JDBC 呼叫,並且不必編寫程式碼來管理關係。它都內建在 EJB 框架中。關係的介面直接透過大多數 EJB 工作人員已經很熟悉的流行的 java.util.Collection 和 java.util.Set 建置。非常酷﹗ 這個額外功能包括對Entity bean 中持久性欄位的 JavaBeans 元件模式的支援。這樣,不用公開您的類別變數 ─ 這總讓我感到很奇怪 ─ 按照我們都知道並喜歡的 JavaBean 的標準命名模式來建立 get 和 set 方法。 我不會過於強調這一點。因為 EJB 2.0 容器將支援大多數通用的 SQL 資料庫(以及其它資料儲存),您可以編寫使用多種類型資料庫的元件。這使得出售要求持久儲存的元件更容易。例如,您可以出售將在使用 Oracle 的 IT 部門或使用 DB2 的商店發揮作用的元件。這樣,不用使用 SQL 編寫低級 JDBC 呼叫(特定於某個特殊的資料庫),您將使用 EJB-QL 來建立尋找程式和選擇方法,並在配置描述子中描述關係。 簡單地說,CMP/CMR 是跨平台元件建立中所缺少的環節。CMP/CMR 將刺激企業級元件市場的增長。此外,CMP/CMR 比低級 JDBC 呼叫更容易使用。CMP/CMR 改正很多不足之處,彌補了 CMR 早期版本中缺少的功能。雖然有很多持久性框架,但是沒有一個能和 EJB CMP/CMR 一樣,在那麼多的應用程式伺服器平臺上可用。 對於本技術教學我需要知道什麼? 本技術教學中的範例可在任何支持 EJB 2.0、遵循 J2EE 的應用程式伺服器上執行。範例程式碼盡可能是規範的;這樣,所有範例程式碼被部署到隨 Java 2 SDK,企業版 1.3 一起提供的 J2EE 參考實現中。只要您的應用程式伺服器支援 EJB 2.0(從而支援 CMP/CMR),那麼只修改 Ant 建立腳本和相應的部署描述子,就能將範例程式碼部署到選擇的應用程式伺服器。 儘管您不必是一位專家,但是本技術教學假設您對 Java 很熟悉,並且對 EJB 有一定程度的瞭解。因為我將講述用 XML 編寫的部署描述子,所以您應該具備 XML 的基礎知識。如果您不熟悉 EJB,我建議您閱讀一篇 developerWorks 技術教學,由 Richard Monson-Haefel 和 Tim Rohaly 合著的 Enterprise JavaBeans Fundamentals(請參閱參考資料)。這是一篇由傑出作者所著的優秀技術教學。即使您不逐字地閱讀該技術教學,我建議您至少將它作為參考。 雖然就本質而言,Ant (一種類似於 make 的基於 XML、開放源代碼的構建系統)方面的知識不是先決條件,但是將有助於您理解範例中出現的構建腳本。 您不需要 JDBC 方面的知識,因為本技術教學中將沒有低級呼叫,但是,SQL 和關聯資料庫理論的基本知識是需要的。 該技術教學系列將講述什麼內容? 我沒有編寫要花費幾天時間來精讀的長篇大論式的技術教學。我將技術教學分為三部分,每部分可以在一小時左右看完。您可以在一個午餐休息時間內完成每篇技術教學的學習,那麼請帶上三明治和飲料,開始學習。 EJB 2.0 加入大量的特性和功能,本技術教學主要討論 CMP/CMR。這樣,本技術教學假設您具有 EJB 和Entity bean 方面的背景知識。要理解本技術教學,您不必是一位 EJB 專家。技術教學講述 local interfaces、配置描述子 CMP、CMR 欄位和關係元素。我們還將全面講解關係類型,如下所示︰
範例中的關係還包括單向支援和雙向支援。這些關係在 XML 配置描述子中定義。 在第 1 部分技術教學中,您將接觸到 CMP/CMR 和 EJB-QL,然後,我將深入到一個簡單的 EJB 2.0 風格的 CMP Entity bean 的範例中。這個範例的一部分展示了在不需要 Java 實作的情況下,如何使用簡單的 EJB-QL 來建立尋找程式方法。本技術教學正是要讓您適應這些術語和技術,技術教學中增加了一個饒有趣味的範例。下一部分,也即第 2 部分技術教學才是重點內容所在。 在第 2 部分技術教學中,我將以第一個範例為基礎,最終講述每一種類型的關係和每一種類型的方向(單向和雙向形式)。每個範例還有一個說明透過存取關係來加入、刪除和變更相關成員的Client。最後,以一對一關係範例和一對多關係範例的結果來說明串聯刪除(cascade-delete)。 在第 3 部分技術教學中,我將說明用進階 EJB-QL 來建置尋找程式和選擇方法。這篇技術教學使用我在前一技術教學中建置的關係來說明 EJB-QL 的來龍去脈。 每個範例包括下面這些程式碼︰配置描述子、實作類別文件、Interface 類別文件、Home Interface 類別文件和一個 Ant 建置腳本。對於這些範例,我將使用隨 Cloudscape RDBMS 系統提供的 J2EE 參考實作。 在這些技術教學將來的後續文章中,我們將把最後一個範例移植到 IBM WebSphere加上 DB2 和 JBOSS 加上HypersonicSQL 環境中,來說明怎樣能將 CMP 2.0 Entity bean 佈署到多種環境︰從輕量級單使用者資料庫到使用完全可伸縮的龐大資料庫的工業級的交易伺服器﹗這篇文章將討論不同 CMP 2.0 實作的一些缺陷。 既然我已經陳述技術教學將講述的內容,那麼我們來簡單地回顧一下我不會講述的內容。我將不會(詳細)講述對交易的支援,因為這對 EJB 2.0 來說並不是新東西。還因為本文主要討論 CMP/CMR,因此沒有必要講述 Message-driven bean 或 Session bean。(Session bean 和交易將出現,但我不作詳細解釋,因為它們在許多其它資源中詳細講述了,要獲取更多的詳細資訊,請參閱參考資料部分。) 請記住,要從本技術教學中獲益,您不必是一位 EJB 專家,而一些細節將留給其它參考資料來說明。雖然由於範例的需要涉及到一些 EJB-QL,但是我們也沒有很詳細地講述 EJB-QL。希望有深入講述 EJB-QL 的後續文章或技術教學。 對於本技術教學您將需要的工具 本技術教學中全都是 EJB 範例,更不用說配置描述子和範例Client了。因此,您將需要 Java SDK 1.3 或更高版本和遵循 J2EE 的應用程式伺服器,參考實作也可以。這裡是必需的工具的清單︰
- 純文字編輯器。IDE 也可以。
- Java 開發環境,比如,Java SDK 1.3 或更高版本。
- 持久性資料儲存,也就是說,很可能是遵循 SQL 的資料庫。
- 您選擇的持久性資料儲存的 JDBC 驅動程式。
- 遵循 EJB 2.0 的 J2EE 應用程式伺服器。
- Ant 建置系統。用來建置和封裝這些範例。
正如上面所陳述的,所有的範例都是在 J2EE 參考實作中完成的,該實作作為 J2EE SDK 1.3 的一部分,可免費得到。 應用程式設計 實體設計 本技術教學使用一個線上內容管理系統的虛擬認證子系統範例,它具有以下這些功能︰
- 將使用者登入到系統
- 認證使用者處於某種角色
- 容許將使用者組織成群組,從而容許進行群組作業
- 儲存諸如位址和連絡訊息這樣的使用者訊息
- 管理角色、使用者和群組
我之所以挑選這個領域是因為大多數工作人員對它有一定程度的瞭解,我希望本技術教學的內容用來講述技術,而不是範例所屬的領域。 圖 1︰我們範例中的 User、Group、Role 和 UserInfo 實體 圖 1 系統中的實體的概述表明有四個截然不同的實體︰User、Group、Role 和 UserInfo。這些實體中的每一個都有下面這三種關係︰
- User 與 Role 相關聯(多對多)
- 一個 User 有一條 UserInfo(一對一)
- 一個 Group 有多個 User(一對多)
在關係資料庫中,這些實體的每一個都很可能都有一個它自己的表格以及一個對應於任何多對多關係的表格。這樣,這個範例應用程式由五個表組成,使用關係資料庫為模型。 Client透過Session bean 存取系統 本範例中的所有Entity bean 都是 local Entity bean。Local bean 不能由遠端Client存取。它們也不能由一個方法返回。這樣,所有對本範例中實體的存取都透過一個 Session bean 來完成,在這個範例中,該Session bean 擔當Client和Entity bean 之間的中介(請參閱圖 2)。 圖 2︰作業和關聯圖 從上面這個圖您可以看到,UserManagement Session bean 可以存取所有其它 bean。它作為進入認證系統的facade 作用。 圖 3︰Client應用程式存取的 UserManagement 的塊圖。 圖 3 說明Client只存取 UserManagementBean 。在本範例中,它不直接存取任何其它 bean。另請注意,Client在另一個位址空間內;實際上它可以在世界上任何地方的另一台電腦上。Client遠端存取Session bean UserManagementBean 。 範例概述 當我閱讀一個技術教學時,有一件事我很討厭︰技術教學中的範例太複雜。我忍受不了陷入那些理解技術所不必要的細節中。 我還喜歡看原始碼,並且盡可能地編譯和執行它。直到您能夠使範例執行,這個範例才是很實在的,您可以瀏覽一下程式碼。請記住這一點,我們把技術教學範例分為難度逐漸增加的範例,每一個範例都有自動的建置文件。這樣,當您第一次檢視 CMP 時,您不必擔心 EJB-QL 等等。由於技術教學逐漸加深難度,我們建議您按照正確的順序進行學習。 範例如下︰
- 第一個範例向您介紹 CMP 的概念,包括在配置描述子中定義 CMP 欄位。這個範例還說明怎樣從另一個 bean 參照企業 bean,以及怎樣從一個Client參照企業 bean。這個範例只有兩個 bean︰
UserBean 和 UserManagementBean 。這個範例在第一篇技術教學,即第 1 部分中。
- 第二個範例使用 EJB-QL 將一個
findAll() 方法新增到 home 介面。這個範例只是對上一個範例稍做變更,目的是說明 EJB-QL。它有同樣數目的 bean。這個範例也在目前這篇技術教學,即第 1 部分中。
- 第三個範例講述了加入
UserInfoBean ,以及在 UserBean 和 UserInfoBean 之間建立一對一的雙向關係。這個範例在第二篇技術教學,即第 2 部分中。
- 在第四個範例中,我們講述了加入
RoleBean ,以及在 UserBean 和 RoleBeans 之間建立多對多的單向關係。這個範例在第二篇技術教學,即第 2 部分中。
- 在第五個範例中,我們講述了加入
GroupBean ,以及在 GroupBean 和 UserBean 之間建立一對多的雙向關係。這個範例在第二篇技術教學,即第 2 部分中。
- 最後一個範例擴充了第五個範例,加入了 EJB-QL 來說明瀏覽物件的集合。這個範例在的第三篇技術教學,即第 3 部分中。
整個技術教學是以程式碼為中心的,每一部分的程式碼都涉及到︰Client、Entity bean 和Session bean。 建立Entity bean CMP 基礎知識︰透過範例來說明 第一個範例向您介紹 CMP 的基本概念,包括下面這些︰
- 在配置描述子中定義 CMP 欄位,
- 怎樣從另一個 bean 參照一個企業 bean,
- 怎樣從一個Client參照一個企業 bean。
在這個範例中,我們將建立三個東西︰
- 一個使用CMP 的Entity bean(
UserBean )。
- 一個存取Entity bean 的Session bean(
UserManagementBean )。
- 一個存取Entity bean 的Client(Section2Client)。
UserBean (Entity bean) UserBean 代表系統中的使用者。UserBean 像所有企業 bean 一樣,需要下面這三個 Java 原始文件︰一個 home、一個介面和一個實作。這樣,我們需要定義下面這些程式碼︰
- local interfaces(
LocalUser )
- local home interfaces(
LocalUserHome )
- Entity bean 類別(
UserBean )
這個範例的原始碼可以在 cmpCmr/section2/src 中找到。要編譯這個範例,請前往 cmpCmr/section2 並在指令提示符後輸入 ant 。將在 cmpCmr/section2/final 目錄中建立一個樣本 .ear。如果您使用的是參考實作,您可以使用 deploytool 程式佈署這個 .ear。 定義 CMP 欄位 在 local interfaces中定義 CMP 欄位 第一步是定義 CMP 欄位。CMP 欄位是您希望 EJB 容器持久儲存的欄位。一個Entity bean 有多個 CMP 欄位,這些欄位是虛欄位。與 CMP 1.0 不同,在 CMP 2.0 中您不給 CMP 欄位定義成員變數。相反,您使用 JavaBeans 屬性命名約定,定義與您希望持久儲存的欄位名相對應的虛 getter 和 setter 方法。在介面和實作中,您都要加入這些抽象方法。下面的程式碼說明了這個概念︰
package com.rickhightower.auth;
import javax.ejb.EJBLocalObject;
public interface LocalUser extends EJBLocalObject {
public String getEmail();
public String getPassword();
}
因為我們給 UserBean 的 email 和 password CMP 欄位只定義了 getter,所以它們都被認為是唯讀欄位。相反,即使屬性是唯讀的,實作Entity bean 也必須給該屬性定義 getter 和 setter 方法。 在Entity bean 類別中定義 CMP 欄位(實作) 與 local interfaces不同,即使 CMP 欄位是唯讀的,實作Entity bean 類別也必須給 CMP 欄位定義 getter 和 setter 方法。CMP 欄位的 getter 和 setter 方法是抽象方法,因為此處的實作將由 EJB 容器定義。實作類別的程式碼如下所示︰
package com.rickhightower.auth;
public abstract class UserBean implements EntityBean {
public abstract String getEmail();
public abstract void setEmail(String email);
public abstract String getPassword();
public abstract void setPassword(String password);
}
請注意Entity bean 被宣告為是抽象的,它的 CMP 欄位定義抽象的 getter 和 setter 方法。(請注意省略號簡化了清單關鍵的部分。) 很可能您將不得不把實體映射到一個資料庫表格並將 CMP 欄位映射到這個表格的列。以下展示一個樣本表格,這個表格可以用來與這個實體相結合。 UserBean 的樣本表格 UserBean 中的 CMP 欄位可以與關係表中的欄位對應,用下面的 SQL DDL 定義︰
CREATE TABLE TBL_USER ( email varchar (50) PRIMARY KEY, password varchar (50) NOT NULL )
請注意,因為從實體到表格的映射留給各個 EJB 容器來做,所以我們在本技術教學中將不講述映射的細節問題。(這將在接下來的文章中講到,該文章將這些範例移植到 WebLogic 和 JBoss 中。)相反,我們使用由 J2EE 參考實作提供的預設映射。 在配置描述子中定義 CMP 欄位 每一個 CMP 欄位必須在配置描述子中定義,如下所示︰ <cmp-field> <field-name>password</field-name> </cmp-field>
<cmp-field> <field-name>email</field-name> </cmp-field>
雖然配置描述子中 CMP 欄位的定義由 EJB 規範指定,但是仍沒有將Entity bean 映射到 SQL 表格並將 CMP 欄位映射到 SQL 列的標準方法。(我希望這在 EJB 規範的下一個版本中得到解決。) 映射由 EJB 容器實作決定。這一點在 EJB 將來的發行版中很可能會改變。因為參考 J2EE 應用程式伺服器的實作不容許輕易地從Entity bean 映射到 SQL 表,所以這個範例採用透過使用 deploytool 所定義的映射。 您將不必完成映射。映射由所包括的 Ant 建置腳本提供。 在配置描述子中定義主索引鍵欄位 每個Entity bean 必須有一個主索引鍵。UserBean 的主索引鍵是 email CMP 欄位。這樣,主索引鍵欄位及其類型必須在配置描述子中指定,就像Entity bean 那樣,如下所示︰ <prim-key-class>java.lang.String</prim-key-class> <primkey-field>email</primkey-field>
配置描述子 您可能期望瞭解配置描述子的其它部分。請注意配置描述子使用 <cmp-version>2.x</cmp-version> 來指定所需要的 CMP 的版本。這一項加入進去,從而適應支援 CMP 1.0 和 2.0 風格的具有容器管理的持久性的 EJB 容器。完整的配置描述子如下所示︰ 清單 1︰UserBean 的配置描述子
<?xml version="1.0" encoding="GB2312"?>
<!DOCTYPE ejb-jar PUBLIC '-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 2.0//EN' 'http://java.sun.com/dtd/ejb-jar_2_0.dtd'>
<ejb-jar>
<display-name>user-mgmt-beans</display-name>
<enterprise-beans>
<entity>
<display-name>UserBean</display-name>
<ejb-name>UserBean</ejb-name>
<local-home>com.rickhightower.auth.LocalUserHome</local-home>
<local>com.rickhightower.auth.LocalUser</local>
<ejb-class>com.rickhightower.auth.UserBean</ejb-class>
<persistence-type>Container</persistence-type>
<prim-key-class>java.lang.String</prim-key-class>
<reentrant>True</reentrant>
<cmp-version>2.x</cmp-version>
<abstract-schema-name>UserBean</abstract-schema-name>
<cmp-field>
<description>no description</description>
<field-name>password</field-name>
</cmp-field>
<cmp-field>
<description>no description</description>
<field-name>email</field-name>
</cmp-field>
<primkey-field>email</primkey-field>
</entity>
</enterprise-beans>
</ejb-jar>
尋找程式和建立方法 建立方法 到此為止,我們講述定義 CMP 欄位。另一個主要特性是 CMP 2.0 定義 findByPrimaryKey() 方法。請注意 Entity bean 類別中缺少 ejbFindByPrimaryKey() ,因為 ejbFindByPrimaryKey() 由 EJB 容器定義。尋找程式方法,findByPrimaryKey() 必須在 home 介面中宣告(在下面列出)。 最後,我們來講述建立 UserBean 的能力。UserBean 實體類別定義了兩個建立方法,ejbCreate() 和 ejbPostCreate() 。因為 email 和 password CMP 欄位是唯讀的(在local interfaces中,email 和 password CMP 欄位沒有 setter 方法),容許使用 ejbCreate() 方法來初始化它們是比較謹慎的,如下所示︰
package com.rickhightower.auth;
public abstract class UserBean implements EntityBean {
public String ejbCreate(String email, String password)
throws CreateException {
setEmail(email);
setPassword(password);
return null;
}
public void ejbPostCreate(String email, String password) { }
} 您要注意的一件事是這些方法的實作實際上是空的。另請注意,ejbCreate() 方法呼叫的 setEmail、setPassword 方法是抽象的;這樣,在它們由 EJB 容器在佈署過程中定義。 請記住,ejbCreate() 方法在容器將行插入到資料庫之前被呼叫,而 ejbPostCreate() 在容器將行插入到資料庫之後被呼叫。因為容器管理Entity bean 的建立,所以 ejbCreate() 方法返回 null。home 介面中適當的建立方法如下所列︰
package com.rickhightower.auth;
import javax.ejb.EJBLocalHome;
import javax.ejb.CreateException;
import javax.ejb.FinderException;
public interface LocalUserHome extends EJBLocalHome {
public LocalUser create (String email, String password)
throws CreateException;
public LocalUser findByPrimaryKey (String email)
throws FinderException;
}
請注意,home 介面建立方法的參數與 ejbCreate() 和 ejbPostCreate() 方法的參數匹配,這是任何經驗豐富的 EJB 工作人員都會料想到的。Entity bean 類別的完整的程式碼清單如下所列︰ 清單 2︰UserBean 的程式碼 package com.rickhightower.auth;
import javax.ejb.EntityBean; import javax.ejb.EntityContext; import javax.ejb.CreateException;
public abstract class UserBean implements EntityBean {
public String ejbCreate(String email, String password) throws CreateException { setEmail(email); setPassword(password); return null; } public void ejbPostCreate(String email, String password) { }
public abstract String getEmail(); public abstract void setEmail(String email); public abstract String getPassword(); public abstract void setPassword(String password); public void setEntityContext(EntityContext context){ } public void unsetEntityContext(){ } public void ejbRemove(){ } public void ejbLoad(){ } public void ejbStore(){ } public void ejbPassivate(){ } public void ejbActivate(){ }
}
我們完成第一個範例的實際工作。那是我們編寫一個 CMP Entity bean 必須做的所有事情;範例 1 的其它部分只說明了怎樣存取,以及怎樣將這個 bean 封裝以便在網路上使用。 CMP 欄位將由容器管理。我們所必須做的是用虛 getter 和 setter 方法來表示 CMP 欄位。 此外,findByPrimaryKey() 方法完全由容器定義。我們必須定義特定於應用程式的建立方法,而建立方法的實作實際上是空的﹗ 我們前面已經講過,CMP Entity bean 通常是local bean。您不能直接從遠端Client存取 local bean。於是,接下來我們定義用來存取這個Entity bean 的Session bean。 建立Session bean UserManagementBean(Session bean) C lient 所有的 CMP 工作反映為 UserManagementBean 。出於各種意圖和目的,這是一個普通的Session bean。 UserManagementBean 有用來建立、刪除以及驗證使用者密碼的操作。目前為止,它是唯一會直接訪問 UserBean 的類別。 圖 4:UserManagement 操作和關聯圖 UserManagementBean 參照 UserBean 為了讓 UserManagementBean 管理使用者,它將不得不尋找 UserBean 的 home 介面。 UserManagement 透過尋找 UserBean 的 home 介面來存取 UserBean 實體,如下所示︰
package com.rickhightower.auth;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
public class UserManagementBean implements SessionBean {
private LocalUserHome getUserHome() throws NamingException {
Context initial = new InitialContext();
return (LocalUserHome) initial.lookup("java:comp/env/ejb/LocalUser");
}
請注意 getUserHome() 方法是一個私有的助手方法(helper method),它返回對 UserBean 方法的 home 介面(LocalUserHome )的參照。為了使 UserManagementBean 能夠存取 UserBean 的 home 介面,必須將下面的內容新增到 UserManagementBean 的配置描述子的 session 元素中,如下所示︰
<session>
...
<ejb-local-ref>
<ejb-ref-name>ejb/LocalUser</ejb-ref-name>
<ejb-ref-type>Entity</ejb-ref-type>
<local-home>com.rickhightower.auth.LocalUserHome</local-home>
<local>com.rickhightower.auth.LocalUser</local>
<ejb-link>userEntity.jar#UserBean</ejb-link>
</ejb-local-ref>
...
請注意 ejb-local-ref 定義對 UserBean 的參照。(UserBean 包括在叫做 userEntity.jar 的 .jar 文件中,我們將在以下詳細講述如何將您的應用程式封裝。) UserManagementBean 獲取 UserBean 的 home 介面 UserManagementBean 將對 home 介面的參照作為私有成員變數儲存。Session bean 在 ejbCreate() 和 ejbActivate 方法的建立和活化期間尋找Entity bean,如下所示︰
package com.rickhightower.auth;
public class UserManagementBean implements SessionBean {
private LocalUserHome userHome = null;
public void ejbCreate() throws CreateException {
try {
userHome = getUserHome();
} catch (NamingException e) {
throw new CreateWrapperException(
"Unable to find local user home in ejbCreate", e);
}
}
public void ejbActivate() {
try {
userHome = getUserHome();
} catch (NamingException e) {
throw new EJBException(
"Unable to find local user home in Activate", e);
}
}
}
這樣 home 介面對於Session bean 的商業方法總是可用的。 遠端介面中的 UserManagementBean 業務方法 在 UserManagementBean 的遠端介面中簡潔地定義 UserManagementBean 的業務方法,如下所示:
package com.rickhightower.auth;
import javax.ejb.EJBObject;
import java.rmi.RemoteException;
public interface UserManagement extends EJBObject {
public void addUser(String email, String password) throws RemoteException;
public void removeUser(String email) throws RemoteException;
public boolean verifyPassword(String email, String password)throws RemoteException;
}
UserManagementBean 商業方法實作 所有 EJB 老手應該意識到這些方法的實作是在實作Session bean 類別中的,如下所示︰
package com.rickhightower.auth;
public class UserManagementBean implements SessionBean {
public void addUser(String email, String password) {
try {
LocalUser user = userHome.create(email, password);
} catch (CreateException e) {
throw new EJBException
("Unable to create the local user " + email, e);
}
}
public void removeUser(String email) {
try {
userHome.remove(email);
} catch (RemoveException e) {
throw new EJBException("Unable to remove the user " + email, e);
}
}
public boolean verifyPassword(String email, String password){
try {
LocalUser user = userHome.findByPrimaryKey(email);
return user.getPassword().equals(password);
} catch (FinderException e) {
throw new EJBException
("Unable to create the local user " + email, e);
}
}
}
UserManagementBean 商業方法分解 請注意 addUser 方法帶有一個 email 和一個 password 參數。然後,這個方法呼叫 LocalUserHome 的建立方法,如下所示︰ public void addUser(String email, String password) {
LocalUser user = userHome.create(email, password);
removeUser 方法使用傳給它的 email 參數來呼叫 LocalUserHome 的刪除方法,從而將 UserBean 從容器中刪除,也就是說,將與 UserBean 相對應的行從資料庫中刪除。由於email 是主索引鍵,因此它可以用來唯一地識別資料實體,用 home 介面的刪除方法來刪除它。提示︰我們並沒有在 home 介面中定義刪除方法 ─ 該方法是從 javax.ejb.EJBLocalHome 介面繼承的,由 EJB 容器實作。 public void removeUser(String email) {
userHome.remove(email);
verifyPassword 方法使用 LocalUserHome 的 findByPrimaryKey() 方法來尋找與 email 主索引鍵相關聯的 UserBean 案例。verifyPassword 方法然後使用實體的 CMP password 欄位來與被作為參數傳遞的 password 進行比較,從而確定它們是否匹配,如下所示︰
public boolean verifyPassword(String email, String password){
LocalUser user = userHome.findByPrimaryKey(email);
return user.getPassword().equals(password);
Session bean 的商業方法使用 UserBean 管理使用者 bean 的案例(加入、刪除),並且用來驗證 password 與所傳遞的 password 是否相同。就 UserManagementBean Session bean 的這一點來說,UserBean Entity bean 沒有什麼特別之處。 UserManagementBean 完整的清單 UserManagement Session bean 沒有什麼真正特別的地方。它參照 UserBean 並呼叫其 home 方法來建立和刪除使用者。它還使用 LocalUser 介面來驗證密碼。 下面列出完整的原始碼和配置描述子、介面以及類別文件。 清單 3︰UserManagementBean 的配置描述子清單(ejb-jar.xml)
<?xml version="1.0" encoding="GB2312"?>
<!DOCTYPE ejb-jar PUBLIC '-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 2.0//EN' 'http://java.sun.com/dtd/ejb-jar_2_0.dtd'>
<ejb-jar>
<display-name>main-user-mgmt</display-name>
<enterprise-beans>
<session>
<display-name>UserManagementBean</display-name>
<ejb-name>UserManagementBean</ejb-name>
<home>com.rickhightower.auth.UserManagementHome</home>
<remote>com.rickhightower.auth.UserManagement</remote>
<ejb-class>com.rickhightower.auth.UserManagementBean</ejb-class>
<session-type>Stateful</session-type>
<transaction-type>Container</transaction-type>
<ejb-local-ref>
<ejb-ref-name>ejb/LocalUser</ejb-ref-name>
<ejb-ref-type>Entity</ejb-ref-type>
<local-home>com.rickhightower.auth.LocalUserHome</local-home>
<local>com.rickhightower.auth.LocalUser</local>
<ejb-link>userEntity.jar#UserBean</ejb-link>
</ejb-local-ref>
</enterprise-beans>
</ejb-jar>
請注意,我們展示所有範例的配置描述子,除了 security 元素和 assembly 元素。 清單 4︰UserManagementBean 完整的遠端介面
package com.rickhightower.auth;
import javax.ejb.EJBObject;
import java.rmi.RemoteException;
public interface UserManagement extends EJBObject {
public void addUser(String email, String password) throws RemoteException;
public void removeUser(String email) throws RemoteException;
public boolean verifyPassword(String email, String password)throws RemoteException;
} 下面展示了 UserManagementBean 的 home 介面和Entity bean 的實作程式碼。 清單 5︰UserManagementBean 的完整 home 介面
package com.rickhightower.auth;
import java.rmi.RemoteException;
import javax.ejb.CreateException;
import javax.ejb.EJBHome;
public interface UserManagementHome extends EJBHome {
UserManagement create() throws RemoteException, CreateException;
}
清單 6︰UserManagementBean 的完整Entity bean 實作。
package com.rickhightower.auth;
import javax.ejb.SessionBean;
import javax.ejb.SessionContext;
import javax.ejb.CreateException;
import javax.ejb.RemoveException;
import javax.ejb.FinderException;
import javax.ejb.EJBException;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import com.rickhightower.util.CreateWrapperException;
public class UserManagementBean implements SessionBean {
private LocalUserHome userHome = null;
public void addUser(String email, String password) {
try {
LocalUser user = userHome.create(email, password);
} catch (CreateException e) {
throw new EJBException
("Unable to create the local user " + email, e);
}
}
public void removeUser(String email) {
try {
userHome.remove(email);
} catch (RemoveException e) {
throw new EJBException("Unable to remove the user " + email, e);
}
}
public boolean verifyPassword(String email, String password){
try {
LocalUser user = userHome.findByPrimaryKey(email);
return user.getPassword().equals(password);
} catch (FinderException e) {
throw new EJBException
("Unable to create the local user " + email, e);
}
}
public void ejbCreate() throws CreateException {
try {
userHome = getUserHome();
} catch (NamingException e) {
throw new CreateWrapperException(
"Unable to find local user home in ejbCreate", e);
}
}
public void ejbActivate() {
try {
userHome = getUserHome();
} catch (NamingException e) {
throw new EJBException(
"Unable to find local user home in Activate", e);
}
}
public void ejbPassivate() {
userHome = null;
}
public void ejbRemove() {}
public void setSessionContext(SessionContext sc) {}
private LocalUserHome getUserHome() throws NamingException {
Context initial = new InitialContext();
return (LocalUserHome) initial.lookup("java:comp/env/ejb/LocalUser");
}
}
建立Client應用程式
/** Client code */
package com.rickhightower.client;
import com.rickhightower.auth.UserManagement;
import com.rickhightower.auth.UserManagementHome;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.rmi.PortableRemoteObject;
public class Section2Client {
public static void main(String[] args) {
try {
Context initial = new InitialContext();
Object ref =
initial.lookup("java:comp/env/ejb/UserManagement");
UserManagementHome home =
(UserManagementHome)PortableRemoteObject
.narrow(ref,UserManagementHome.class);
/* Create the users--Rick and Nick */
UserManagement userMgmt = home.create();
userMgmt.addUser("[email protected]", "mypassword");
userMgmt.addUser("[email protected]", "oxford");
/* Verify Rick's Password */
boolean login = false;
login = userMgmt.verifyPassword("[email protected]",
"mypassword");
System.out.println("Login =" + login);
/* Remove the users--Rick and Nick */
userMgmt.removeUser("[email protected]");
userMgmt.removeUser("[email protected]");
} catch (Exception e) {
System.err.println("MESSAGE:" + e.getMessage());
e.printStackTrace(System.err);
}
}
}
<?xml version="1.0" encoding="GB2312"?>
<!DOCTYPE application-client PUBLIC '-//Sun Microsystems, Inc.//DTD
J2EE Application Client 1.3//EN'
'http://java.sun.com/dtd/application-client_1_3.dtd'>
<application-client>
<display-name>Section2Client</display-name>
<ejb-ref>
<ejb-ref-name>ejb/UserManagement</ejb-ref-name>
<ejb-ref-type>Session</ejb-ref-type>
<home>com.rickhightower.auth.UserManagementHome</home>
<remote>com.rickhightower.auth.UserManagement</remote>
<ejb-link></ejb-link>
</ejb-ref>
</application-client>
|
|
UserManagement Client 為使這篇技術教學比較完整,我們必須有一個可以練習我們的範例的Client。這個應用中的Client完成下面這些工作︰
- 使用 JNDI 尋找
UserManagement Session bean
- 使用
UserManagementBean 的 addUser 方法將兩個使用者新增到系統中
- 使用
UserManagementBean 的 verifyPassword() 方法驗證其中一個使用者的密碼是不是“mypassword”
- 然後使用
UserManagementBean 的 removeUser 方法刪除這兩個使用者
Client的完整的程式碼清單列在左邊。 為了使應用程式Client能夠存取Session bean,它的配置描述子中(application-client.xml)需要一個 ejb-ref 元素項目,在 home 介面清單的左下部的範例欄中顯示這個配置描述子。 請注意,我們從Client存取Session bean 的方式與 UserManagement Session bean 存取使用者實體的方式相似。 此外,如果您希望包括Client的 .jar 文件是可執行的,那麼您必須在清單文件(MANIFEST.MF)中指定Client的類別檔案名稱作為 .jar 文件的主類別,如下所示︰
Manifest-Version: 1.0
Main-Class: com.rickhightower.client.Section2Client
|
編譯和包裝應用程式 編譯和包裝您的應用程式 既然我已經展示所有支援 CMP 的Entity bean 的程式碼,那麼我們來看一看怎樣包裝以及實際怎樣對其進行佈署。為了便於佈署,我建立了一個 Ant 建置腳本,它完成下面這些作業︰
- 編譯所有的原始碼
- 將企業 bean 包裝到兩個 .jar 文件中︰一個是Session bean 的(userMgmt.jar),一個是所有實體的(userEntity.jar)
- 將Client程式碼包裝到 .jar 文件(client-userMgmt.jar)中
- 使用 SQL DDL 來建立樣本資料庫表格
- 將 .jar 文件包裝到 .ear 文件(app.ear)中
請注意 .ear 文件是企業壓縮檔案。基本上它是一個可以包括其它 .jar 和 .war 的文件。透過使用隨參考實作提供的 deploytool 版本,可以讀和處理 ear 文件。 為了編譯和包裝應用程式,您將需要重新設定您的環境。如下所示設定環境︰
set USR_ROOT=c:
set JAVA_HOME=%USR_ROOT%\jdk1.3
set J2EE_HOME=%USR_ROOT%\j2sdkee1.3
set ANT_HOME=%USR_ROOT%\tools\Ant
PATH=%PATH%;%ANT_HOME%\bin;%J2EE_HOME%\bin
上面的範例假設您正在使用 Windows 系統,並且您已經在 c:\jdk1.3 安裝了 Java SDK,在 c:\j2sdkee1.3 安裝了 J2EE SDK,以及在 c:\tools\ant 安裝了 Ant 建置應用程式。請建立一個類似於上面所列出的文件的批處理文件,並且適當地調整目錄位置。在其它平臺上的開發人員將不得不進行修改。(我想任何 Unix 程式員都能夠輕而易舉地將上面的文件轉換為他們所選的 shell 腳本,因此我就不妄加指導以免小看了他們的智慧。) 用 deploytool 部署您的應用程式 要運行 Ant 建構腳本,請轉到 cmpCmr/seciton2,然後在命令提示符後輸入 ant package 。使用不帶參數的 Ant 將顯示一條消息,描述運行 Ant 腳本的所有選項,如下所示: 運行不帶參數的 Ant 建構腳本的輸出:
It is best to set the J2EE_HOME environment variable
before running this build script.
To compile the example use Ant as follows:
ant compile
To compile and package the examples use the following:
ant package
To create sample tables for this example use the following:
ant createTables
最後一步(SQL DDL)更適用于具有健壯的實體,從而能進行 SQL 表格映射的 EJB 實現,而參考實現則沒有這樣健壯的實體。我們將其留在這裏是以防您想要將範例移植到您選擇的應用程式伺服器。它只是初級的 SQL DDL 代碼,並且只像參考實現建立表格那樣為您建立第一個表格。 只要您使用 Ant 構建腳本來編譯源代碼,請將類別打包到 jar 中,並將 jar 打包到 .ear 文件中,然後您會希望用 deploytool 打開 .ear 檔,這將在下一章中描述。 大多數應用程式伺服器都帶有佈署工具。這些工具通常使您能夠佈署應用程式並將Entity bean 和 CMP 欄位映射到資料庫的表格和列。參考實作沒有什麼不同。它帶有一個叫做 deploytool 的佈署工具。 其餘部分假設您將使用參考實作和 Ant 來編譯、包裝和佈署您的應用程式。請在必要的地方對您的應用程式伺服器進行調整。關於怎樣設定環境的說明,請參閱前一章。 為了佈署 .ear 文件,您將需要執行 J2EE 參考實作以及 Cloudscape RDBMS 系統。一旦 J2EE 應用程式伺服器和 Cloudscape 執行起來,您將想要執行所描述的佈署工具,如下所示︰
rem calls a batch file that defines proper environment variables
call setenv
cd j2sdkee1.3
cd bin
start "CLOUDSCAPE" cloudscape -start
start "J2EE SERVER" j2ee -verbose
start "DEPLOY TOOL" deploytool
一旦一切執行起來,您就可以使用 deploytool 開啟位於 cmpCmr/section2/final 中的 .ear 文件(app.ear),如下圖所示。 圖 5︰用 deploytool 開啟 EAR 文件 deploytool 一開啟 .ear 文件,您就可以瀏覽它。請檢視 .ear 文件,看看Client的 .jar 文件,Session和Entity bean 包括在 .ear 文件中,如下圖所示︰ 圖 6︰用 deploytool 顯示 .ear 文件中的 .jar 文件 另請注意,如果您在樹視圖中選擇 UserBean ,然後在細節視圖中選擇 Entity 頁籤,那麼您就可以看到 UserBean 的 CMP 欄位,如下圖所示︰ 圖 7︰用 deploytool 顯示 UserBean 中的 CMP 欄位 一旦您瀏覽完 .ear 文件,那麼您就可以佈署它了。要佈署 .ear 文件,請前往工具選單,然後選擇 Deploy... 選單項。在佈署精靈的第一步,請選中核取方塊 Return Client Jar,然後點擊 finish 按鈕,將 .ear 文件佈署到容器中。(請注意參考實作必須正在執行,請參閱 J2EE SDK 文件以獲取更多的詳細資訊 ─ 另請參閱下面關於設定環境的說明。) 圖 8︰用 deploytool 佈署 .ear 文件 請注意 deploytool 展示了佈署在樹視圖中 Servers/localhost 元素中的 .ear 文件。另請注意 deploytool 將一個Client應用程式 .jar 文件存放在 cmpCmr/section2/final 目錄中。下一步是實際執行Client,這將在下一節中描述。 要執行Client,請在指令提示符下前往 cmpCmr/section2/final,然後在指令提示符後輸入下面的指令。 runclient -client app.ear -name Section2Client-textauth
參考實作Client預設的使用者名稱和密碼分別是 guest 和 guest123。SessionClient輸出看起來像下面這樣︰
C:\cmp-cmr\section2\final>runclient -client app.ear -name Section2Client -textauth
Initiating login ...
Username = null
Enter Username:guest
Enter Password:guest123
Binding name:`java:comp/env/ejb/UserManagement`
class type com.rickhightower.auth._UserManagementHome_Stub
Login =true
Unbinding name:`java:comp/env/ejb/UserManagement` 關於 Ant 建置腳本的詳細資訊 為了使本技術教學比較完整,下面講到了 Ant 建置腳本。如果您對這個主題不感興趣,您完全可以略過這一主題,繼續本技術教學其餘內容的學習,而不會有什麼問題。 作者提示 由於我希望逐漸增加應用程式的難度,我發現很有必要建立一個 Ant 建置腳本。起初,我試圖只使用 deploytool 。但是我發現在每一次變更之後,為了重新佈署應用程式,我不得不重複一個 50 步的過程。我還發現每一步之後,讀者將不得不重複同樣的 50 步的過程。於是我建立了一個 Ant 建置腳本,從而使將範例建置到 .ear 文件中的工作自動化。完成這個工作的關鍵是將 deploytool 建立的文件放回到各種子元件的 META-INF 目錄中。請注意,50 步可能看起來有一點誇張,可能是吧,但即使是按順序執行 12 步也是非常費時且容易出錯的。 請閱讀 Ant 建置腳本的描述元素以瞭解它執行什麼工作。因為 Ant 建置腳本是用簡單的 XML 編寫的,並且您已經領會了它執行什麼工作的要點,建置腳本應該比較容易理解。 清單 9︰Ant 建置腳本
<?xml version="1.0" encoding="GB2312"?>
<project basedir="." default="about" name="cmp-cmr">
<property environment="myenv" />
<property name="driver" value="com.jnetdirect.jsql.JSQLDriver"/>
<property name="driver-classpath" value="/cvs/lib/JSQLconnect/JSQLConnect.jar"/>
<property name="password" value="cruelsummer"/>
<property name="userid" value="user1"/>
<property name="url" value="jdbc:JSQLConnect://CANIS_MAJOR:1433/database=auth"/>
<property name="j2ee-lib" value="${myenv.J2EE_HOME}/lib/j2ee.jar"/>
<property name="outdir" value="/tmp/cmp-cmr"/>
<property name="final_output" value="./final"/>
<!-- relative to outdir -->
<property name="client" value="${outdir}/ejb-jar-client"/>
<property name="build" value="${outdir}/ejb-jar"/>
<property name="meta-inf" value="${build}/META-INF"/>
<property name="dist" value="${outdir}/dist"/>
<property name="lib" value="${outdir}/lib"/>
<property name="jar_name_user" value="userEntity.jar"/>
<property name="jar_name_user_mgmt" value="userMgmt.jar"/>
<property name="app_ear" value="app.ear"/>
<target description="prepare the output directory." name="prepare">
<mkdir dir="${build}"/>
<mkdir dir="${lib}"/>
<mkdir dir="${meta-inf}"/>
<mkdir dir="${client}"/>
<mkdir dir="${dist}"/>
<mkdir dir="${final_output}"/>
</target>
<target name="dropTables">
<sql classpath="${driver-classpath}" driver="${driver}"
password="${password}" url="${url}" userid="greek">
DROP TABLE TBL_USER
</sql>
</target>
<target name="createTables">
<sql classpath="${driver-classpath}" driver="${driver}"
password="${password}" url="${url}" userid="greek">
CREATE TABLE TBL_USER (
email varchar (50) PRIMARY KEY,
password varchar (50) NOT NULL
)
</sql>
</target>
<target depends="prepare"
description="compile the Java source."
name="compile">
<echo> J2EE lib is set to ${j2ee-lib}</echo>
<javac destdir="${build}" srcdir="./src">
<classpath>
<pathelement location="${j2ee-lib}"/>
<pathelement location="${junit-lib}"/>
</classpath>
</javac>
</target>
<target depends="compile"
description="package the Java classes into a jars."
name="package">
<copy todir="${client}">
<fileset dir="${build}">
<patternset id="Client">
<exclude name="**/*Bean*"/>
</patternset>
</fileset>
</copy>
<!-- jar the client -->
<jar basedir="${client}" jarfile="${lib}/client.jar"/>
<!-- jar the entity bean -->
<jar jarfile="${dist}/${jar_name_user}">
<fileset dir="./src/META-INF/Entity" />
<fileset dir="${build}">
<patternset id="UserEntity">
<include name="**/*LocalUser*"/>
<include name="**/*UserBean.class"/>
</patternset>
</fileset>
</jar>
<!-- jar the session bean -->
<jar jarfile="${dist}/${jar_name_user_mgmt}">
<fileset dir="./src/META-INF/Session" />
<fileset dir="${build}">
<patternset id="UserMgmt">
<include name="**/*LocalUser*"/>
<include name="**/*UserManagement*.class"/>
<include name="**/*CreateWrapperException.class"/>
</patternset>
</fileset>
</jar>
<!-- jar the client as per refernce implementation -->
<jar jarfile="${dist}/client-${jar_name_user_mgmt}">
<fileset dir="./src/META-INF/App-Client" />
<fileset dir="${build}">
<patternset id="App-Client">
<include name="**/*Client*"/>
<include name="**/*UserManagement*.class"/>
<exclude name="**/*Bean*"/>
</patternset>
</fileset>
</jar>
<!-- jar the jars into an ear -->
<jar jarfile="${final_output}/${app_ear}">
<fileset dir="./src/META-INF/Application" />
<fileset dir="${dist}">
<patternset id="Application">
<include name="**/*.jar"/>
</patternset>
</fileset>
</jar>
</target>
<target name="clean" depends="prepare"
description="clean up the output directory.">
<delete dir="${outdir}" />
<delete dir="${final_output}" >
<patternset id="clean" >
<include name="**/*.ear"/>
<include name="**/*.jar"/>
</patternset>
</delete>
</target>
<target name="about" >
<echo>
It is best to set the J2EE_HOME environment variable
before running this build script.
To compile the example use Ant as follows:
ant compile
To compile and package the examples use the following:
ant package
To create sample tables for this example use the following:
ant createTables
</echo>
</target>
</project>
關於 .ear 文件的應用程式配置描述子的詳細資訊 同樣是為使這篇技術教學比較完整,下面是 .ear 文件配置描述子,它列出了所有用到的模組,如下所示︰ 清單 10︰.ear 文件配置描述子(application.xml) <?xml version="1.0" encoding="GB2312"?>
<!DOCTYPE application PUBLIC '-//Sun Microsystems, Inc.//DTD J2EE Application 1.3//EN' 'http://java.sun.com/dtd/application_1_3.dtd'>
<application> <display-name>section2</display-name> <description>Example tutorial written for IBM developerWorks by www.rickhightower.com</description>
<module> <ejb>userEntity.jar</ejb> </module> <module> <ejb>userMgmt.jar</ejb> </module>
<module> <java>client-userMgmt.jar</java> </module> </application>
關於將應用程式移植到其它 EJB 容器的說明 請注意,雖然這個範例使用參考實作,但是為了適應任何遵循 EJB 2.0 的容器,稍微地修改建置腳本和配置描述子不會花太多功夫。 EJB 容器越遵循規範,就越容易完成轉換。 另請注意,如果您想使用建置腳本,那麼為了與您的資料庫匹配,您必須變更 JDBC 驅動程式和 JDBC URL 屬性。這樣,您可能不得不實際變更的所有內容是下面這些︰
<property name="driver-classpath" value="/cvs/lib/JSQLconnect/JSQLConnect.jar"/>
<property name="password" value="cruelsummer"/>
<property name="userid" value="user1"/>
<property name="url" value="jdbc:JSQLConnect://CANIS_MAJOR:1433/database=auth"/>
這時,您應該有這個範例應用程式的可執行版本。接下來,我們將用 EJB-QL 加入尋找程式方法。 尋找程式方法和 EJB-QL Enterprise JavaBeans Query Language(EJB-QL),SQL92 的子集,容許在配置描述子中為CMP Entity bean 尋找程式方法。EJB-QL 是功能強大的小型語言,它易於學習和使用。像 SQL 一樣,EJB-QL 易於學習,但是要熟練掌握需要花費一些時間。 EJB-QL 被擴充為能夠瀏覽 CMR 關係。EJB 容器負責將 EJB-QL 查詢轉換為持久資料儲存的查詢語言,例如,IBM DB2 的 SQL-92。這種轉換使得能夠建立可移植性更好的尋找程式方法。這使得企業元件供應商可以出售在更多目標平臺上工作的元件。本系列的第三篇技術教學將詳細講述 EJB-QL。這裡只是稍微提一下。
/** Home interface now has findAll method **/
package com.rickhightower.auth;
import java.util.Collection;
public interface LocalUserHome extends EJBLocalHome {
public Collection findAll() throws FinderException;
}
<entity>
<display-name>UserBean</display-name>
<ejb-name>UserBean</ejb-name>
<query>
<description></description>
<query-method>
<method-name>findAll</method-name>
<method-params />
</query-method>
<ejb-ql>select Object(theUser) from UserBean as theUser</ejb-ql>
</query>
</entity>
|
|
建立 UserBean 的 findAll() 方法 尋找程式方法容許按照某個標準來尋找實體。尋找程式方法在 home 介面中定義。在 EJB 2.0 之前,由工作人員來建立他自己的尋找程式方法(或者專有的 EJB 供應商解決方案)。工作人員將在 home 介面中宣告尋找程式方法,比如 findAll() ,然後工作人員將在 bean 實作中定義尋找程式方法適當的定義,例如 ejbFindAll() 。 有了 EJB 2.0 CMP,您只需使用 EJB-QL 在配置描述子中定義尋找程式方法的定義。您仍然還必須在 home 介面中宣告這個尋找程式方法。請注意尋找程式方法 public Collection findAll() throws FinderException 是在 home 中宣告的。 另請注意,這個尋找程式方法 findAll() 是使用 query 元素定義的。query 元素定義方法名稱以及方法參數,在這個範例中方法名稱是 findAll() ,沒有參數。請注意 ejb-ql 元素體定義實際的查詢為 select Object(theUser) from UserBean as theUser 。這種語法與 SQL 非常相似。 接下來我們將示範透過實作 getUsers() 方法使用Session bean 的 findAll() 方法。 |
/** Add the getUsers method to the interface */
public interface UserManagement extends EJBObject {
public Collection getUsers() throws RemoteException;
}
/** Add the getUsers method to the implementation */
public class UserManagementBean implements SessionBean {
public Collection getUsers(){
ArrayList userList = new ArrayList(50);
Collection collection = userHome.findAll();
Iterator iterator = collection.iterator();
while(iterator.hasNext()){
LocalUser user = (LocalUser)iterator.next();
userList.add(user.getEmail());
}
return userList;
}
|
|
將 getUsers() 方法新增到 UserManagementBean 中
為了使用 home 介面的 findAll() 方法,我們將 getUsers() 方法新增到 UserManagementBean 中。UserManagementBean 類別中的 getUsers() 實作呼叫 findAll() 方法。然後它迭代整個 LocalUser 物件集合,並將它們的 email 屬性放在 ArrayList 中。因為不能遠端地傳送本地 bean,getUsers() 方法不能簡單地返回由 findAll() 傳送的 Collection 。 |
public class Section2Client {
public static void main(String[] args) {
Collection collection = userMgmt.getUsers();
Iterator iterator = collection.iterator();
while(iterator.hasNext()){
String email = (String)iterator.next();
System.out.println("user id=" + email);
}
}
}
|
|
使用來自Client的 getUsers() 方法 Client程式碼呼叫 getUsers() 方法。然後它獲得從 getUsers() 方法返回的唯一識別資料使用者的電子信件的集合,並將它們列印到標準輸出。 |
編譯和佈署 findAll() EJB-QL 範例 用來建置、包裝和佈署第一個範例的同樣技術可以用來建置、包裝和佈署 findAll 範例,這些技術可以在 編譯和包裝您的應用程式這一單元中找到。這個範例的所有原始碼和建置文件都可以在 cmpCmr/section2.2 中找到。 請務必執行 ant clean 來刪除舊範例的中間建置文件。 結論 我們完成最前面的兩個範例。所有其它的範例都是基於這兩個範例的。 我講述 CMP Entity bean 的定義;建立參照和使用Entity bean 的Session bean;並建立參照和使用Session bean 的Client。然後,透過將一個尋找程式方法新增到實體的 bean home 介面,我對Entity bean 進行擴充。尋找程式方法的實作是在Entity bean 配置描述子中使用 EJB-QL 定義的。 如果您對此已經理解得很透徹,恭喜您﹗您已經登峰造極。所有比這裡的主題更簡單的內容都可以迎刃而解。 本系列的下一篇技術教學將講述定義容器管理的關係。 參考資料
- 關於 EJB CMP/CMR 和 EJB-QL 的優秀技術教學 http://www.caucho.com/products/resin-ejb/ejb-tut/cmp-tut.xtp
- Sun 的 J2EE 技術教學 http://developer.java.sun.com/developer/onlineTraining/J2EE/Intro/
- Developer's Guide to Understanding EJB 2.0(由 Rick Hightower 更新)http://www.triveratech.com/dloads/index.html
- Enterprise JavaBeans fundamentals http://www-105.ibm.com/developerWorks/education.nsf/java-onlinecourse-bytitle/EB2ADE177F8C3EF386256A0A006DCBCD?OpenDocument
- Ed Roman、Scott W. Ambler、Tyler Jewell、Floyd Marinescu 合著的 Mastering Enterprise JavaBeans (2nd Edition),EJB 的百科全書﹗ http://www.amazon.com/exec/obidos/ASIN/0471417114
- Richard Monson-Haefel 所著的 Enterprise JavaBeans (3nd Edition),也請買下這本書﹗ http://www.amazon.com/exec/obidos/ASIN/0596002262
- Richard Hightower、Nicholas Lesiecki 合著的 Java Tools for Extreme Programming,講述了用 EJB 建置和佈署 J2EE。http://www.amazon.com/exec/obidos/ASIN/047120708X
作者 Rick Hightower,eBlox 的開發主管,具有十多年軟體工作人員經驗。他領導採用新過程(比如XP)和新技術(比如 CMP 和 CMR)。
Rick 的出版品包括 Java Tools for eXtreme Programming,它講述佈署和測試 J2EE 工程(由 John Wiley 出版);參與合著的 Java Distributed Objects(由 Sams 出版);還有 Java Developer's Journal 中的幾篇文章。 |