EJB设计模式

一:

第一个设计模式非常简单。一个公司和雇员的Entity Bean
下面给出的Entity Bean的代码片断是类似的。它们是由jbuilder4
EntityBean
模版生成的。所有的字段都声明为publiccmp字段。

Code snippet for Company Entity Bean
public class CompanyBean implements EntityBean {
EntityContext entityContext;
public Integer comId; //the primary key
public String comName; //the company name
public String comDescription //basic description
public Timestamp mutationDate //explained later
public Integer ejbCreate() throws
CreateException {
return null;
}
//various get() and set() for every column/field
// which are exposed in the Remote Interface as well
Code snippet for Employee Entity Bean
public class EmployeeBean implements EntityBean {


EntityContext entityContext;
public Integer empId; //the primary key
public Integer comId; //the company foreign key
public String empFirstName; //the employee firstname
public String empLastName // the employee lastname
public Timestamp mutationDate //explained later
public Integer ejbCreate() throws
CreateException {
return null;
}



//various get() and set() for every column/field
// which are exposed in the Remote Interface as well
这个设计模式虽然很简单,但是却有很多缺点,比如,对每一个
字段的访问都会导致对get()set()方法的一次远程调用。而远
程过程调用(RPCs)是非常耗费资源的,并且,对于在实际中通
常要求的组合的访问会导致一系列的远程调用。可以说,这个模
式在实际中可用性很差。上面展示的设计模式可以作为其他设计
模式的基础,比如RAD,原型设计,测试等。这时,那个代表雇
员的Employee Entity Bean并没有展示出在雇员和公司之间有何
关系。
 
二:

为了避免设计模式1的缺点,我们介绍一下封装
entity bean
值域的value objec的概念。value object
用某些语言的术语来说,就是一个结构类型,因为他们
corba的结构类型非常类似。

value Object code snippet for Company
public class CompanyStruct implements
java.io.Serializable {
public Integer comId; //Primary Key
public String comName;
public String comDescription;
public java.sql.Timestamp mutationDate;
}
value Object code snippet for Employee
public class EmployeeStruct implements
java.io.Serializable {
public Integer empId; //Primary Key
public Integer comId; //Foreign Key
public String empFirstName;


public String empLastName;
public java.sql.Timestamp mutationDate;
}




现在,公司和雇员的entity bean可以把上面的一个结构类型作为
ejbCreate()
的一个参数。由于这个结构封装了entity的所有字段
的值,entity bean只需要一个getdata()setdata()方法就可以
对所有的字段进行操作。

Code snippet for an Entity Bean’s create()
public Integer ejbCreate(CompanyStruct struct) throws
CreateException {
this.comId = struct.comId;
this.comName = struct.comName;
this.comDescription = struct.comDescription;
this.mutationDate = struct.mutationDate;
return null;
}
Code snippet for an Entity Bean’s getData()
public CompanyStruct getData() {
CompanyStruct result = new CompanyStruct();
result.comId = this.comId;
result.comName = this.comName;
result.comDescription = this.comDescription;


result.mutationDate = this.mutationDate;
return result;
}
Code snippet for an Entity Bean’s setData()
public void setData(CompanyStruct struct) {
this.comName = struct.comName;
this.comDescription = struct.comDescription;
this.mutationDate = struct.mutationDate;;
}



跟设计模式1中使用单独的get()set()方法去操作特定字段不同,
在设计模式2中,我们避免这种情况而只需要进行一次远程调用就
可以了。现在,只有一个事务通过一次远程调用就操作了所有的数
据。这样,我们就避免了设计模式1的大部分缺点,除了建立bean
之间的关系外。
虽然setdata()方法可以对所有字段赋值,但是,borland appserver
提供了一种智能更新的特性,只有被修改过的字段才会被重新写入数
据库,如果没有字段被修改,那么ejbStore()方法将会被跳过。
borland程序员开发指南(EJB)有更详细的描述。
同样,在entity beanstruct之间存在这重复的代码,比如同
样的字段声明。这意味着任何数据库表结构的修改都会导致
entity beabn
struct的改变,这使得同步entitystruct变得
困难起来。


就是在ebCreate()方法中调用setddata()方法,这可以消除一
些冗余的代码。

Code snippet for an Entity Bean’s create()
public Integer ejbCreate(CompanyStruct struct) throws
CreateException {
this.comId = struct.comId; //set the primary key
setData(struct);//this removes some redundant code
return null;
}
 


三:

在设计模式2中我们看到,在entity beanstruct之间
有很多重复的代码比如同样的字段声明(对应数据库中的表列)。
如果让entity bean从结构继承下来就可以避免冗余的代码。但是
这种设计,仍然不能显示beans之间的联系。

Code snippet for Company Entity Bean
public class CompanyBean extends CompanyStruct
implements EntityBean {
EntityContext entityContext;
//all fields in CompanyStruct are available for CMP
public Integer ejbCreate(CompanyStruct Struct)
throws CreateException {
this.comId = struct.comId; //set the primary key
setData(struct);//this removes some redundant code
return null;
}


其余的代码比如getdata()setdata()方法的实现和设计模式2
是完全一样的。


四:

在设计模式3中我们看到使beanstruct继承后使得代码大
幅缩水并且所有的字段都可定义为cmp字段。这里,我们可
以更进一步修正setdata()getdata()的实现方法来减少代码量。
我们为这个struct增加一个方法。

value Object code snippet for Company
public class CompanyStruct implements
java.io.Serializable {
public Integer comId;
public String comName;
public String comDescription;
public Timestamp mutationDate;
public void copyFrom(CompanyStruct struct) {
comId = struct.comId;
comName = struct.comName;
comDescription = struct.comDescription;
mutationDate = struct.mutationDate;
}
}




由于entity bean是从struct继承下来的,在bean的实现类
中也一样可以引用copyfrom()方法,当然,必须注意的是,
这个copyfrom()方法并不是一个商业方法,它不需要在bean
的远程接口中暴露给调用者。
现在,getdata()setdata()方法可以简化更进一步的简化。

Code snippet for an Entity Bean’s getData()
public CompanyStruct getData() {
CompanyStruct result = new CompanyStruct();
result.copyFrom(this);
return result;
}



这里把this作为一个参数传入copyfrom()。由于enttity bean
struct继承而来,于是这个entitty bean便可以作为一个
struct
传入。
EJB
容器并不赞成把this指针作为一个参数传递因为在两个控
制线程中同时访问一个bean的实例可能会引起事务冲突。但事
实上我们所做的并没有违背这个原则,因为我们的并没有在
bean
之间传递this的引用并且也没有引用任何可能引起事务冲突的方法。

Code snippet for an Entity Bean’s setData()
public void setData(CompanyStruct struct) {
this.copyFrom(struct);
}





对于一个映射到有很多列的表的entity bean,这种实现
方法的优点是使得bean实现类的代码非常简单。这种设
计模式使得代码及其精简,可读性和可维护性也大大增强。
任何数据库的修改都只需要修改作为基类的struct,而几
乎不需要修改bean的代码。把这种改变从struct分离出来,
cmp字段发生改变时需要修改部署描述符。这就使得开
发时能够更好的适应设计的改变。
这里,还是没有实现bean之间的关系,这将在设计模式5中解决。
 
五:

就像我们在设计模式4中看到的, Entity Bean的实现大小被缩减到在ejbCreate(), getData()and setData()方法中的仅仅几行,不管CMP字段的数目.下一步是建模公司和雇员的Entity Beans,这个有点繁琐而且建议读者先对borland公司的的OR Mapping和高级CMP有所了解.
对这个关系建模根本不需要对结构的代码变化,然而Entity Beans实现类需要一点点修改来反映两个实体间的关系,鉴于此Deployment Descriptor需要有小的修改.

象以前, Entity Bean从结构继承,下面是公司Entity Bean的代码片段:

public class CompanyBean extends CompanyStruct
implements EntityBean {
EntityContext entityContext;
// CMP for all fields in the CompanyStruct
public java.util.Collection employees; //one-to-many
//rest of the code including getData() and setData()
public java.util.Collection getEmployees() {
return employees;
}
}





下面是雇员Entity Bean的程序片段:

public class EmployeeBean extends EmployeeStruct
implements EntityBean {
EntityContext entityContext;
//CMP for all fields in EmployeeStruct EXCEPT
//the comId
public Company company;//remote reference to company
}




在上面的程序片段中,雇员Entity Bean从雇员结构继承,雇员结构本身有
一个字段comId表示雇员和公司之间的的外键,在所有的前面的设计模式中,
这个字段是CMP.而在设计模式5中这个字段用在Deployment Descriptorun-checking的方法从CMP中去掉.而对公司Entity Bean的远程引用现在是CMP.现在的问题是怎么在getData()SetData()方法中更新公司Entity Bean的引用,当这些方法只getset comId(在设计模式上下文中没有被CMP)的值.简单的说,过程的结构没有变化并且字段comId(不再CMP)RPC中被拷贝到Entity Bean和从Entity Bean拷贝出来.需要的是对公司Entity Bean的远程引用在必须被写入数据库和从数据库读出时更新.我们需要用ejbLoad()ejbStore()方法在Entity Bean实现类中为我们完成这项工作.
在雇员Entity Bean中的ejbLoad()方法的代码片段如下:


public void ejbLoad() {
try {
comId=(company ==


null)?null:(Integer)company.getPrimaryKey();
} catch (Exception e) {
//throw some runtime exception (e.g. EJBException)
}
}



以上代码几乎不需要解释.当数据被从数据库中读出(在事务的开始时候),
comId(
不是CMP)字段在雇员Entity Beanset.因此当getData()方法被调用时,返回的结构将包含正确地comId的值.
在雇员Entity Bean中的ejbStore()方法如下:

public void ejbStore() {
try {
company = (comId ==
null)?null:beanGlossary.getCompanyHome().findByPrimary
Key(comId);
} catch (Exception e) {
//throw some runtime exception (e.g. EJBException)
}
}



ejbStore()
在事务结束当数据被写入数据库时被调用.在这种情况下,comId的值被修改(通过调用setData方法),this必须被写到数据库中.在上面方法中的代码把comId转化成公司的远程引用.(毕竟comId是公司Entity Bean的主键).
使用空check的原因是数据库不能存空值(表之间的弱引用),并且这些同样需要建模.


任何情况下,java对基本类型的封装要比使用基本类型自己好,因为他们能
存空值而且易于转换成其他形式.
上面的BeanGlossary类的代码片断容易引起一些混淆.
这实际上是一个捕获EJBlookuputility(一个无状态session bean),
entity bean和有状态session bean的情况下,Home接口的lookup是被缓冲的.在无状态session bean的情况下,Remote接口是被缓冲的(作为ejb规范1.1的一部分,一个SLSBHome接口中调用的create()是不被优化的).
通过在上面上下文的缓冲,我们意思是第一个请求是被lookup.随后的调用是得到已经在对象引用中初始化的home接口或remote接口.
BeanGlossarySB utility
类的代码片段如下:

public class BeanGlossarySB implements SessionBean {
private Context context = null;
public javax.naming.Context getContext() throws
NamingException {
if (context == null)
context = new javax.naming.InitialContext();
return context;
}
// Company
private CompanyHome companyHome = null;
public CompanyHome getCompanyHome() throws
NamingException {


companyHome = ((CompanyHome)
javax.rmi.PortableRemoteObject.narrow(
getContext().lookup("java:comp/env/ejb/Company"),
CompanyHome.class));
return companyHome;
}
// rest of the EJBs
}



在设计模式5,我们没有处理Entity BeanHome接口.
在雇员Entity Bean的情况下会有一个finder元素在
findEmployeesByCompany(Company pCompany)
的几行中,
这将会返回雇员远程引用的集合在公司Entity Bean 中的Deployment
Descriptor map
了在上面定义的finder元素的雇员集合.
这样,在公司Entity Bean中的方法getEmployees()remote接口中的调用
返回需要的与那家公司相联系的远程引用的雇员的集合.
 

 

你可能感兴趣的:(java)