J2EE SDK自帶了一個Cloudscape數據庫。用我們呆會兒提到的實體Bean例子來訪問這個自帶的數據庫,并不需要我們另外去設定環境變量和參數。甚至在本例子中,并不需要你去寫SQL語句和JDBC的代碼就可以執行數據庫的訪問操作。是不是很神奇?其實,這些都是由J2EE的配置工具在配置實體Bean的時候產生了表和SQL代碼。
- 創建一個實體Bean
一個實體Bean代表了數據表中一行持久的數據。當創建一個實體Bean的時候,其代表的數據就寫到相應的數據表中。產生一個新行。如果實體Bean的數據被更新,數據表中相應的行也要更新。所有這些表的創建和行的創建都不需要客戶來寫SQL代碼。
即使在服務器崩潰的情況下,實體Bean的數據也能保存下來,這就是所謂的實體Bean數據的持久性。如果在更新實體Bean的數據的時候服務器崩潰,數據庫中的數據就是最后一次保存的數據。如果是在一個事務進行的時候服務器崩潰了,事務就必須回滾,免得部分提交導致數據庫中數據被破壞。
BonusHome接口:這個Home接口和上個例子的會話Bean的Home接口最大的不同就是這個例子里面增加了findByPrimaryKey這個方法。這個finder方法把主鍵作為一個參數值。這個例子里面是社會保險號:
creat方法將獎金值和主鍵做為參數。當BonusServlet來初始化Home接口并且調用其create方法的時候。容器生成一個BonusBean的實例,并且調用實例的ejbCreate方法。這里,Home接口的create方法和Bean實例的ejbCreate方法必須具有相同的參數。這樣,才能夠將主鍵通過容器從Home接口傳遞到Bean的實例。如果給定的主鍵在數據表里面有相應的記錄,就會觸發一個java.rmi.RemoteException異常。
以下是BonusHome的代碼:
package Beans;
import java.rmi.RemoteException;
import javax.ejb.CreateException;
import javax.ejb.FinderException;
import javax.ejb.EJBHome;
public interface BonusHome extends EJBHome {
public Bonus create(double bonus, String socsec)
throws CreateException, RemoteException; public Bonus findByPrimaryKey(String socsec)
throws FinderException, RemoteException;
}
Bonus接口:創建了Home接口以后,容器就創建remote接口和實體Bean。Bonus接口里面聲明了getBonus方法和getSocSec方法。這樣Servlet就可以使用這兩個方法從實體Bean中得到數據。
其代碼如下:
package Beans;
import javax.ejb.EJBObject;
import java.rmi.RemoteException;
public interface Bonus extends EJBObject {public double getBonus()
throws RemoteException;
public String getSocSec()
throws RemoteException;
}
下面用圖來說明會話Bean和實體Bean的不同:
BonusBean:
這是一個容器管理持續性的實體Bean。就是說由容器來處理和數據庫打交道的操作以及一些事務處理的操作。不需要由客戶編寫Bean數據和數據庫里的數據進行轉換的代碼。
當Servlet調用BonusHome的create方法的時候,容器調用BonusBean的setEntityContext方法。傳遞給setEntityContext方法的實體上下文EntityContext有一個返回指向它本身的refenrence。也可以得到其對應的主鍵值。
然后,容器調用ejbCreate方法,ejbCreate方法把數據值分配給Bean實例的變量。容器將這些數據寫到數據庫里面去。在ejbCreate方法之后,容器調用ejbPostCreate方法。這個簡單的例子沒有ejbPostCreate方法。
另一個空方法是calback方法,由容器調用它來通知Bean,告訴Bean有事件要發生。如果是用的Bean管理持續性的實體Bean,寫Bean的人必須要提供這些方法的實現代碼。另外,如果你需要提供Bean自己的退出的清除代碼和建立時初始化代碼。這些清除和初始化的操作就會在Bean的生命周期的某個適當的時候被容器所調用。下面是這些空方法的簡單描述。
- ejbPassivate和ejbActivate方法:在Bean從存貯器調入調出,進行交換的時候,容器調用這兩個方法。這個過程和虛擬存貯中頁面的調入調出的概念有點相似。
- ejbRemove方法:在客戶調用Home接口的remove方法的時候,由容器調用相應的ejbRemove方法。
- ejbLoad和ejbStore方法:在Bean的數據和數據庫中的數據執行同步的時候,容器調用這兩個方法。
getBonus和getSocSec方法是客戶用來從Bean的實例變量中取數據的方法。本例子沒有用來往Bean實例變量寫數據的set方法。當然,如果有的話,客戶用這樣的方法來設定Bean實例變量數據值。所有的Bean實例中的變量的值的改動都會使數據庫中相應的記錄的數據的改動。
package Beans;
import java.rmi.RemoteException;
import javax.ejb.CreateException;
import javax.ejb.EntityBean;
import javax.ejb.EntityContext;
public class BonusBean implements EntityBean {public double bonus;
public String socsec;
private EntityContext ctx;
public double getBonus() {return this.bonus;
}
public String getSocSec() {return this.socsec;
}
public String ejbCreate(double bonus,String socsec)throws CreateException{
//Called by container after setEntityContext
this.socsec=socsec;
this.bonus=bonus;
return null;}
public void ejbPostCreate(double bonus,String socsec) {//Called by container after ejbCreate
}
//These next methods are callback methods that
//are called by the container to notify the
//Bean some event is about to occur
public void ejbActivate() {//Called by container before Bean
//swapped into memory}
public void ejbPassivate() {//Called by container before
//Bean swapped into storage}
public void ejbRemove() throws RemoteException {//Called by container before
//data removed from database}
public void ejbLoad() {
//Called by container to
//refresh entity Bean’s state}
public void ejbStore() {
//Called by container to save
//Bean’s state to database}
public void setEntityContext(EntityContext ctx){
//Called by container to set Bean context
}
public void unsetEntityContext(){
//Called by container to unset Bean context
}
}
改變Servlet:
BonusServlet的代碼和上一個例子很相似,只是在init方法和doGet方法里面有點改動。這個例子的init里面既查詢了CalcBean這個會話Bean,也查詢了BonusBean這個實體Bean。
public void init(ServletConfig config)
throws ServletException{ try {
InitialContext ctx = new InitialContext();
Object objref = ctx.lookup(“bonus”);
Object objref2 = ctx.lookup(“calcs”);
homebonus=(BonusHome)PortableRemoteObject.narrow(objref, BonusHome.class);
homecalc=(CalcHome)
PortableRemoteObject.narrow(objref2, CalcHome.class);} catch (Exception NamingException) {
NamingException.printStackTrace();
}
}
doGet方法中的try中的代碼段里面創建了CalcBean和BonusBean的Home接口。在調用了calcBonus中的方法計算獎金之后,再調用BonusHome的create方法來產生一個實體Bean的實例,并且在數據庫中相應的表中生成一行數據。在創建了數據表之后,BonusHome的findByPrimaryKey可以從數據表中取出與主鍵相同的一條記錄。然后,將結果通過HTML頁面的形式返回給瀏覽器。
在catch代碼段里面處理了主鍵的重復的情況。數據表里面不能存在兩行具有相同關鍵字的記錄。所以,如果將重復的主鍵傳遞給容器,Servlet就會在創建實體Bean之前捕獲到這個異常。這時,Servlet把重復的主鍵值,和傳遞進來的原始參數用HTML頁面的形式返回給瀏覽器。
編譯實體Bean:
先設定環境變量:
Classpath = J2EE安裝目錄libj2ee.jar
J2EE_HOME = J2EE的安裝目錄把例子放到J2EE的安裝目錄下面的Beans子目錄下:然后在J2EE的安裝目錄下執行:
javac Beans/BonusBean.java Beans/BonusHome.java Beans/Bonus.java
編譯Servlet:
把Servlet放到J2EE的安裝目錄下的ClientCode子目錄下,然后在J2EE安裝目錄下執行:
javac BonusServlet.java
編譯好之后,就該啟動服務平臺和配置工具了:
這個例子需要用到的有:J2EE服務、配置工具、Cloudscape數據庫。除了前面兩個環境變量以外,還需要將環境變量JAVA_HOME 設為 JDK的安裝目錄然后在J2EE的安裝目錄下的in目錄下執行:
j2ee -verbose
deploytool
cloudscape -start把三個服務啟動。
配置步驟:
- 首先是更新Application:
Web包里面包括了BonusServlet和bonus.html文件。因為我們改變了BonusServlet,所以必須用新的BonusServlet代碼來更新應用程序。先在Local Application Window中選擇BonusApp,然后在Tools菜單中選擇Update Application Files就可以了。
- 創建一個實體Bean
創建實體Bean和上個例子中的創建會話Bean非常相似。也是先在Files菜單中選New Enterprise Bean,然后在Add類文件的時候把Bonus.class,BonusHome.class,BonusBean.class三個類文件加入。但要注意Bean的類型這次應該選為Entity實體。
在Entity Setting對話框中,應該選擇:Container-Managered persistence。在下面的窗口中,把bonus和socsec兩個復選框都選上。下面的Primkey key class中填上java.lang.String。注意,Primary Key的類型必須是類的類型,而不能是原始類型。然后Next,一直到事務管理Transaction Management對話框。
選擇容器管理事務:Container-managed transaction。
在下面的各個方法中,將create,findByPrimaryKey,getBonus,getSocSec這些方法設成required。這以為著在調用這些方法之前,容器要啟動一個新的事務。正好在這些方法結束的前面要調用事務的commits方法。然后將BonusApp的JNDI設為BonusBean。
在配置應用程序以前。先要設定實體Bean的SQL代碼:
在左邊先選擇實體Bean:BonusBean,在右邊選擇標簽Entity。然后單擊Deploymetn Setting按紐。將Database JNDI name 設為jdbc/Cloudscape。還要確定Create table on Deploy和Delete table on Undeploy兩個復選框被選上。然后就可以單擊Generate SQL,產生SQL代碼了。(如果產生了數據庫連接錯誤,那就是因為你沒有啟動數據庫服務器。在J2EE的安裝目錄下的in下,啟動:Cloudscape -start)
SQL代碼產生好之后,OK就可以了。
最后就是運行這個應用程序了。注意J2EE的默認端口號是8000
在瀏覽器的地址欄中輸入:http://localhost:8000/BonusRoot/bonus.html
結果和上個例子應該是一樣的。這里,如果你兩次輸入了重復的關鍵字,將得到如下類似的頁面:
Bonus Calculation
Soc Sec passed in: 777777777
Multiplier passed in: 2
Bonus Amount calculated: 200.0
Duplicate primary key.