譯者前言
有關本章中相應源代碼的下載,請參看一個Session Bean的示例
正文
數據是絕大多數商業應用程序的核心。在J2EE應用程序中,entity bean反映了存儲在一個數據庫中的商業對象。對於使用bean管理持續化的entity bean,你必須編寫代碼以訪問數據庫。盡管編寫這樣的代碼會增加一些額外的工作量,但是與此同時,你對entity bean如何訪問數據庫也獲得了更多的控制。
在這一章中,我們將討論一個使用bean管理持續化的entity bean的編程問題。對於entity beans的相關概念,請參閱Entity Bean是什麼?.
SavingsAccountEJB示例
在這一部分的這個entity bean表現了一個簡單的銀行帳戶。SavingsAccountEJB的狀態存儲在一個關系型數據庫的savingsaccount表中。savingsaccount表是用下面的SQL語句建立的:
CREATE TABLE savingsaccount
(id VARCHAR(3)
CONSTRAINT pk_savingsaccount PRIMARY KEY,
firstname VARCHAR(24),
lastname VARCHAR(24),
balance NUMERIC(10,2));
SavingsAccountEJB示例需要以下代碼:
1、Entity bean類(SavingsAccountBean)
2、Home接口(SavingsAccountHome)
3、Remote接口(SavingsAccount)
此外,這個示例還用到以下類:
1、一個名為InsufficientBalanceException的功能類
2、一個名為SavingsAccountClient的客戶端類
在j2eetutorial/examples/src/ejb/savingsaccount目錄下有這個示例的源代碼。要編譯這個代碼,到j2eetutorial/examples目錄下並輸入ant savingsaccount。在j2eetutorial/examples/ears下有SavingsAccountApp.ear文件的示例。
Entity Bean類
在示例程序中,entity bean類名為SavingsAccountBean。在你浏覽它的代碼時,請注意它滿足了所有使用bean管理持續化的entity bean的必要條件。首先,它實現了以下幾個方面:
1、EntityBean接口
2、零個或多個ejbCreate和ejbPostCreate方法
3、Finder方法
4、商業方法
5、Home方法
另外,一個使用bean管理持續化的entity bean類必須滿足這些條件:
1、類定義為public。
2、類不能定義為abstract或final。
3、包含一個空的構造函數。
4、它不能實現finalize方法。
EntityBean接口
EntityBean接口繼承自實現了Serializable接口的EnterpriseBean接口。EntityBean接口中聲明了許多方法,例如ejbActivate和ejbLoad,這些方法你必須在你的entity bean類中加以實現。我們將在下面對這些方法作詳細討論。
ejbCreate方法
當客戶端調用create方法時,EJB容器調用相應的ejbCreate方法。典型的情況是,一個entity bean中的ejbCreate方法執行以下任務:
1、將實體狀態添加到數據庫中
2、對實例變量進行初始化
3、返回主鍵
SavingsAccountBean的ejbCreate方法通過調用private類型的insertRow方法將實體狀態添加到數據庫中,這樣做的結果是執行了一個INSERT語句。下面是ejbCreate方法的源代碼:
public String ejbCreate(String id, String firstName,
String lastName, BigDecimal balance)
throws CreateException {
if (balance.signum() == -1) {
throw new CreateException
("A negative initial balance is not allowed.");
}
try {
insertRow(id, firstName, lastName, balance);
} catch (Exception ex) {
throw new EJBException("ejbCreate: " +
ex.getMessage());
}
this.id = id;
this.firstName = firstName;
this.lastName = lastName;
this.balance = balance;
return id;
}
盡管SavingsAccountBean類只有一個ejbCreate方法,但是一個enterprise bean可以包含多個ejbCreate方法。示例請參見j2eetutorial/examples/src/ejb/cart目錄下的CartEJB.Java。
在為一個entity bean編寫ejbCreate方法,必須遵守以下規則:
1、訪問控制修飾必須是public。
2、返回類型必須是主鍵。
3、參數類型必須滿足Java 2 RMI API。
4、方法修飾不能是final或static。
throws子句可以包含javax.ejb.CreateException的你的應用程序中所指定的其它例外。如果輸入的參數無效,一個ejbCreate方法通常會拋出一個CreateException。如果因為已經存在另一個相同主鍵的實體,ejbCreate方法不能建立一個新的實體,它會拋出一個javax.ejb.DuplicateKeyException(CreateException的子類)。如果一個客戶端接受到一個CreateException或者是一個DuplicateKeyException,它會認為實體未被建立。
可以通過一個對於J2EE服務器未知的應用程序將一個entity bean的狀態直接插入到數據庫中。例如,可以使用一個SQL腳本在savingsaccount表中添加一行。盡管對應於這一行的entity bean不是由一個ejbCreate方法創建的,但是客戶端程序還是可以對這個bean進行定位。
ejbPostCreate方法
對於每一個ejbCreate方法,你必須在entity bean類中編寫一個ejbPostCreate方法。EJB容器在調用ejbCreate方法後會立即調用ejbPostCreate方法。與ejbCreate方法不同,ejbPostCreate方法可以調用EntityContext接口中的getPrimaryKey方法和getEJBObject方法。有關getEJBObject方法的詳細信息,請參看傳遞一個Enterprise Bean的對象索引。不過,你的ejbPostCreate方法常常會是一個空方法。
ejbPostCreate方法必須滿足以下條件:
1、參數的數量和類型必須與相應的ejbCreate方法相匹配。
2、訪問控制修飾必須是public。
3、方法修飾不能是final或static。
4、返回類型必須是void。
throws子句可以包含javax.ejb.CreateException和你的應用程序中所指定的例外。
ejbRemove方法
一個客戶端可以通過調用remove方法刪除一個entity bean。這個調用會導致EJB容器調用ejbRemove方法,該方法會從數據庫中刪除這個實體狀態。在SavingsAccountBean類中,ejbRemove方法調用一個名為deleteRow的private方法,這樣做的結果是執行了一個DELETE語句。ejbRemove方法的源碼比較短:
public void ejbRemove() {
try {
deleteRow(id);
catch (Exception ex) {
throw new EJBException("ejbRemove: " +
ex.getMessage());
}
}
如果ejbRemove方法遇到一個系統問題,它會拋出javax.ejb.EJBException。如果遇到的是一個應用程序錯誤,它會拋出一個javax.ejb.RemoveException。有關系統例外和應用程序例外的比較。請參看處理例外。
直接使用數據庫刪除也可以刪除一個entity bean。例如,如果一個SQL腳本刪除了包含一具entity bean狀態的行,相應的entity bean也會被刪除。
ejbLoad方法和ejbStore方法
如果EJB容器需要將一個entity bean的實例變量與存儲在數據庫中的相應的值進行同步,它會調用ejbLoad方法和ejbStore方法。ejbLoad方法會根據數據庫中的值刷新實例變量,而ejbStore方法會將實例變量的值寫入到數據庫中。客戶端不能調用ejbLoad方法和ejbStore方法。
如果一個商業方法與一個事務關聯,容器會在執行商業方法前調用ejbLoad。而在商業方法執行後,EJB容器會立即調用ejbStore。因為容器會調用ejbLoad和ejbStore,所以你不需要在你的商業方法中刷新和存儲實例變量。SavingsAccountBean類依靠容器進行實例變量和數據庫的同步。因此,SavingsAccountBean的商業方法必須與事務關聯。
如果ejbLoad和ejbStore不能在底層數據庫中定位一個實體,它們會拋出javax.ejb.NoSUChEntityException。這個例外是EJBException的一個子例。因為EJBException是RuntimeException的一個子類,所以你不需要在throws語句中包含它。如果NoSuchEntityException被拋出,EJB容器會在將其返回到客戶端前將其包裝到一個RemoteException中。
在SavingsAccountBean類中,ejbLoad調用了loadRow方法,這樣做的結果是執行了一個SELECT語句並將得到的值重新指派給實例變量。ejbStore調用了storeRow方法,這樣做的結果是通過一個UPDATE語句將實例變量存儲到數據庫中。下面是ejbLoad方法和ejbStore方法的源代碼:
public void ejbLoad() {
try {
loadRow();
} catch (Exception ex) {
throw new EJBException("ejbLoad: " +
ex.getMessage());
}
}
public void ejbStore() {
try {
storeRow();
} catch (Exception ex) {
throw new EJBException("ejbStore: " +
ex.getMessage());
}
}
Finder方法
finder方法允許客戶端定位一個entity bean。在SavingsAccountClient程序中,可以通過三個finder方法定位entity bean:
SavingsAccount jones = home.findByPrimaryKey("836");
...
Collection c = home.findByLastName("Smith");
...
Collection c = home.findInRange(20.00, 99.00);
對於每一個客戶端可用的finder方法,entity bean類都必須實現一個相應的以ejbFind為前綴的方法。例如,在SavingsAccountBean類中,ejbFindByLas
public void ejbLoad() {
try {
loadRow();
} catch (Exception ex) {
throw new EJBException("ejbLoad: " +
ex.getMessage());
}
}
public void ejbStore() {
try {
storeRow();
} catch (Exception ex) {
throw new EJBException("ejbStore: " +
ex.getMessage());
}
}
Finder方法
finder方法允許客戶端定位一個entity bean。在SavingsAccountClient程序中,可以通過三個finder方法定位entity bean:
SavingsAccount jones = home.findByPrimaryKey("836");
...
Collection c = home.findByLastName("Smith");
...
Collection c = home.findInRange(20.00, 99.00);
對於每一個客戶端可用的finder方法,entity bean類都必須實現一個相應的以ejbFind為前綴的方法。例如,在SavingsAccountBean類中,ejbFindByLas