下面两篇从五个方面来介绍如何编写Enterprise bean的客户端:
一、Enterprise JavaBean的客户视图:
Enterprise JavaBean客户是一个独立的应用程序,或者是一个Servlet,或者一个Applet,还或者另一个Enterprise JavaBean。无论如何,客户要使用一个Enterprise JavaBean必须要做如下事情:
首先要JavaBean的home接口:EJB 规范里面要求客户应该使用Java的名字和目录接口JNDI(Java Naming and Direcotry Interface)API来定位Bean的home接口。
然后获得Enterprise JavaBean的remote接口的引用:可以使用Enterprise bean的home接口中定义的方法。可以产生一个会话bean,也可以产生或寻找(find)一个实体bean。
调用Enterprise JavaBean中定义的方法:客户并不直接调用Enterprise JavaBean中定义的方法。而是通过调用在Enterprise JavaBean的remote接口中暴露给用户的方法来使用JavaBean类中的方法。在remote接口中定义的方法就是在JavaBean中希望暴露给用户,让用户使用的方法。
初始化客户:
在IAS所带的例子里面,作为客户的SortClient应用程序引入了必要的JNDI类、还引入了SortBean的home接口和remote接口。客户使用JNDI的API来定位Enterprise JavaBean的Home接口。
定位Home接口:
客户用JNDI定位home接口。首先需要获得一个JNDI的初始化的上下文context。下面的代码实例化了一个新的javax.naming.Context类。在我们这个例子中叫做initialContext。然后,客户就可以使用上下文的lookup()方法从一个名字对应到home接口。注意,初始的命名上下文工厂的初始化是由EJB容器/服务指定的。
上下文的lookup()方法返回了一个java.lang.Object的对象。代码必须把返回的对象转成所希望的类型。下面的代码是SortClient例子中的一段代码。Main()从使用JNDI服务开始,并用上下文的lookup()方法来定位home接口。将remote接口的名字(在这个例子中是sort)传递给context.loopup()方法。注意,程序最后将context.lookup()方法的结果传递给了SortHome,home接口的类型。
用JNDI定位home接口:
// SortClient java
import javax.naming.InitialContext;
import SortHome; // 引入bean的home接口
import Sort; // 引入bean的remote接口
public class SortClient {
……
public static void main(String[] args) throws Exception
javax.naming.Context context;
{ // 用命名服务获得JNDI上下文
context = new.javax.naming.InitialContext();
}
Object objref = context.lookup(sort);
SortHome home = (SortHome)javax.rmi.PortableRemoteObject.narrow(objref,
SortHome class);
Sort sort = home.create();
//做排序工作
sort remove();
}
maint()首先抛出一个普通的异常Excepion。当代码执行到这儿的时候,就算可能会导致程序的终止,客户也需要捕获任何发生的异常。
获得remote接口:
现在我们获得了Enterprise JavaBean的home接口,然后,使用home接口的create()方法或者finder()方法就可以获得其remote接口了。实际使用哪个方法取决于Enterprise JavaBean的类型和Enterprise JavaBean的提供者在home接口定义的方法。
例如:上面的代码显示了客户SortClient如何获得Sort的remote接口的引用。一旦SortClient获得了home接口的引用并将之转化为适当的类型(这里是SortHome),客户就可以产生bean的实例,并调用它的方法。本例子中调用了home接口的create()方法,方法返回了remote接口的引用。(因为这个例子里面SortBean是一个无状态的会话bean,其home接口只有一个create()方法,这个方法没有任何参数)。然后,SortClient就能够调用定义在remote接口中的方法:sort()和merge()来进行排序工作。当排序工作完成的时候,客户调用remote接口的remove方法来删除Enterprise bean的实例。
会话Bean:
客户通过调用home接口中的Create()方法获得Enterprise JavaBean的remote接口的引用。
每一个会话Bean都必须至少有一个Create()方法。无状态的会话bean只能有一个Create()方法,并且这个Create()方法不能有参数。有状态的Session bean可以有一个不带参数的Create()方法,也可以有另外的带有不同参数的create()方法。Create()方法中的参数是用来初始化的会话bean的。
缺省的create()方法没有参数。例如,Sort例子使用了一个无状态的会话bean。根据定义,它有一个没有参数的create()方法。
Sort sort=home.create();
我们比较一下另一个例子:Cart例子,使用的是有状态的会话bean。在其home接口中实现了多个的create()方法。其中一个create()方法带了三个参数,三个参数用来标识cart的购买者,返回了Cart的home接口的引用。Cart客户先将三个参数的值给cartHoldName,creditCartNumber和expirationDate这三个变量,然后调用create()方法。其代码如下:
如何调用create()方法的代码:
Cart cart;
{
String cartHolderName=”Jack B.Quick”
String creditCartNumber = “345-346-32525252”
Date expirationDate = new GregorianCalendar(2001, Calendar JULY, 1) getTime();
Cart = home.create(cartHolderName,creditCartNumber,expirationDate);
}
另外,要注意的是,会话Bean没有finder方法。
实体 bean:
通过finder方法,或者通过create方法,客户得到实体对象的引用。我们在前面提到,实体对象代表了存储在数据库中的某些数据。既然Entity bean代表了一些持久的数据,那么通常实体bean存在相对长的一段时间,肯定比客户调用它的时间要长,这样,客户通常需要寻找一个代表了数据库中它感兴趣的那一段数据的实体bean,而不是创建(create)一个实体bean。Create操作将会产生一个新的数据,并将新的数据存在底层的数据库中。
客户通过find操作来定位一个已经存在的实体bean。可以是相关数据库里面指定的行。也就是说,finder操作定位一个原来已经插入到数据库中的数据实体。这个数据也许已经被实体bean加入到数据库中,也许已经被加入到EJB上下文的外部(例如是数据库管理系统DBMS)。或者是在原有系统中,这数据的在安装EJB之前就已经存在。
客户使用create方法产生一个新的数据实体,并将数据实体存放在底层的数据库中。实体bean的create方法将实体数据插入到数据库中,用create方法的参数初始化实体的变量。实体bean的create方法总是返回一个remote接口。但是,对应的ejbcreate()方法是返回实体Bean实例的主键。
每一个实体bean都必须有一个主键值来唯一标识它。一个实体bean也可以有次键值,用来定位特定的实体对象。
Find方法和主键类:
一个实体bean的缺省的find方法是findByPrimaryKey()方法。它通过主键来定位实体bean。其语法如下:
<remote interface> findByPrimaryKey(<key type> primary key)
每一个实体bean必须实现findByPrimaryKey方法。PrimaryKey参数是一个独立的定义在配置描述器中的主键类。键的类型是主键,而且必须是RMI-IIOP合法的值。键类可以是任何Java类或者你自己写的类。
例如,你可能有一个名字为Acount的实体bean。已经将它的主键类定义为AccountPK。AccountPK是一个字符串类型。它拥有Account bean的标识。我们将account bean的标识设为AccountPK,然后调用findByPrimaryKey方法,就可以得到account bean实例的引用。如下代码所示:
Finding a Entity bean instance using the primary key
AccountPK accountPK =new AccountPK(“1234-56-789”);
Account source = accountHome.findByPrimaryKey(accountPK);
Bean的提供者可以定义另外的finder方法给客户使用。
Create()方法和remove()方法:
客户也能够用home接口中定义的create()方法来创建一个实体bean。当客户调用实体bean的create()方法的时候,一个新的实体bean的实例就存到数据仓库中。新实例总是有一个标识它的主键值。它的状态可以用create方法传递的参数值来初始化。
注意:实体bean存在的时间与它在数据库中代表的数据是一样的。其生命并不由与客户的会话来决定。通过调用实体bean的remove方法,可以将实体bean删除。Remove方法不仅将实体bean删除,也将它所代表的数据从数据库中删除。另外也可以直接删除实体bean。例如,可以通过用原来的应用程序或用DBMS删除数据库中的一条记录的方法来删除。
调用方法:
一旦客户获得了bean的remote接口的引用。就可以调用在remote接口中为enterprise bean定义的方法。属于此bean的应用逻辑的方法才是客户感兴趣的。也有一些方法是用来获得enterprise bean的信息或者接口信息的。例如:获得bean对象的handle,检查一个bean是否与另一个bean相同,删除bean的方法。
下面的代码解释了客户如何调用enterprise bean中的方法。这个例子调用的是Cart 这个会话bean。代码从产生一个新的Cart bean实例并重新找到一个cart bean实例的remote接口的引用开始。从这里开始,客户就准备好可以调用bean的方法。
首先,客户创建了一个新的book对象,设定了其title 和price参数。然后,调用了enterprise bean的应用方法addItem()来将一个book对象加到shopping cart中去。AddItem()方法是在CartBean 这个会话bean中定义的,通过Cart的remote接口把它变成公共的方法。客户增加了一项其它的东西,然后用summairze()方法列出shopping cart里面的所有的Items。最后,就调用remove方法来删除bean的实例。注意,客户用调用其他的方法也是通过同样的途径,即使是自己定义的方法summarize()。
调用Bean的方法
………
Cart cart;
{
…
//获得bean的remote接口的实例。
Cart = home.create(cartHodlerName,creditCardNumber,expirationDate);
}
//创建一个新的book对象。
Book knuthBook = new Book(“The Art of Computer Programming”,49.95f);
//将新书加到Cart中。
Cart.addItem(knutBook);
………
//列举当前Cart中的书
summarize(cart);
cart.removeItem(knutBook);
……
删除bean的实例:
remove方法对会话bean的操作和对实体bean的操作是不同的。因为对于客户来说,会话对象的存在是不持久的。会话对象的客户在完成了会话之后需要调用remove方法。对客户来说,有两个remove方法可以调用:可以用javax.ejb.EJBObject.remove()方法来删除会话bean;也可以用javax.ejb.EJBHome.remove(Handle handle)方法来删除会话bean的handle。
一个好的编程风格是不需要由客户删除一个会话bean对象。如果客户没有删除一个有状态的会话bean,容器在过了一段时间(指定的超时值timeout value)之后会自动删除这个对象。Timeout value是一个配置属性。但是,客户能够为以后的引用保持这个对象的handle。
Entity bean的客户不需要考虑这个问题,因为实体bean只是在客户的事务期间。由容器负责它的生命周期,包括激活和钝化。只是当客户需要将实体对象从底层数据库中删除的时候,才调用remove方法。
|