ilamei
在这次的教程中,我们将上次的用会话EJB:Calc换成实体EJB。作为客户的BonusServlet调用实体Bean,将社会保险号和奖金的信息存到数据库中去,在另一个时候,还可以从数据库中把这些信息重新取出来。数据库访问将第四层加到瘦客户端,成为了四层体系机结构的企业应用程序。
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.
(出处:http://www.knowsky.com)