全面研读 EJB 2.0
2001-04-05· --·wsdn
EJB 2.0 中的一个示例 CMP 实体
在 EJB 2.0 中,容器管理的实体 bean 被定义为抽象的,而且它的持久性字段并不在 bean 类中直接定义。作为替代,开发了一种抽象的持久性方案,从而允许 bean 提供者间接地声明持久性字段和 bean 关系。下面是 Employee bean 的一个示例,它使用了新的抽象持久性方案。请注意,该 bean 类中未声明任何持久性字段。
public abstract EmployeeBean implements
javax.ejb.EntityBean {
. // 实例字段
EntityContext ejbContext;
// 容器管理的持久性字段
public abstract void setIdentity(int identity);
public abstract int getIdentity();
public abstract void setFirstName(String firstName);
public abstract String getFirstName();
public abstract void setLastName(String lastName);
public abstract String getLastName();
// 容器管理的关系字段
public abstract void
setContactInfo(ContactInfo info);
public abstract ContactInfo
getContactInfo();
...
}
在此 bean 的 XML 部署描述符中,抽象的持久性方案声明容器管理的各个字段和各种关系。
<ejb-jar>
<enterprise-beans>
<entity>
<ejb-name>EmployeeEJB</ejb-name>
...
<persistence-type>Container</persistence-type>
...
<cmp-field><field-name>identity</field-name></cmp-field>
<cmp-field><field-name>firstName</field-name></cmp-field>
<cmp-field><field-name>lastName</field-name></cmp-field>
...
</entity>
</enterprise-beans>
<dependents>
<dependent>
<dependent-class>ContactInfo</dependent-class>
<dependent-name>ContactInfo</dependent-name>
<cmp-field>street</cmp-field>
<cmp-field>city</cmp-field>
<cmp-field>state</cmp-field>
<cmp-field>zip</cmp-field>
<cmp-field>homePhone</cmp-field>
<cmp-field>workPhone</cmp-field>
<cmp-field>email</cmp-field>
...
</dependent>
<relationships>
<ejb-relation>
<ejb-relation-name>Employee-ContactInfo</ejb-relation-name>
<ejb-relationship-role>
<ejb-relationship-role-name>
employee-has-contactinfo
</ejb-relationship-role-name>
<multiplicity>one</multiplicity>
<role-source>
<ejb-name>EmployeeEJB</ejb-name>
</role-source>
<cmr-field>
<cmr-field-name>contactInfo</cmr-field-name>
<cmr-field-type>ContactInfo</cmr-field-type>
</cmr-field>
</ejb-relationship-role>
<ejb-relationship-role>
<ejb-relationship-role-name>
contactinfo_belongsto_employee
</ejb-relationship-role-name>
<multiplicity>one</multiplicity>
<role-source>
<dependent-name>ContactInfo<dependent-name>
</role-source>
</ejb-relationship-role>
</ejb-relation>
</relationships>
<ejb-jar>
用来描述容器管理的关系的 XML 元素可能变得非常复杂,因为他们必须处理各种关系的对应性和方向(单向的还是双向的)。上面的代码段说明,为了描述 bean 与其从属对象类之间的简单关系,您需要哪些元素。虽然即使是简单的关系也会被转换为冗长的 XML,但所有这些元素都是必需的,以便持久性管理器能够将复杂的对象图映射到数据库中。
虽然用于定义 CMP bean 的抽象持久性方案的 XML 元素是 EJB 2.0 中的 CMP 的主要问题,但为了简洁起见,本文不再提供 XML 示例。作为替代,本文将纯粹依靠 bean 类中必须使用的抽象习语,来说明 EJB 2.0 中的 CMP 背后的基本概念。这些代码习语与 XML 部署描述符中的关系元素一起使用,并由后者定义,所以您不能只有其一而没有另一个,但它们比该方案的 XML 部分较容易理解。
除了 XML 元素之外,抽象的持久性方案还定义了一组习语,它们在声明 bean 类及其相关的对象时必然会用到。用来访问和修改字段的方法是严格定义了的,要求用 set<:METHOD> 方法修改持久性字段,而用 get<:METHOD> 方法访问它们。这些方法的名称和返回类型由部署描述符中它们相应的 XML 关系元素规定。
实体 bean 类和从属类都遵循相同的抽象持久性方案。下面是如何将 ContactInfo 对象定义为从属对象类的示例。
public abstract class ContactInfo {
file:// 家庭地址信息
public abstract void setStreet(String street);
public abstract String getStreet();
public abstract void setState(String state);
public abstract String getState();
public abstract void setZip(String zip);
public abstract String getZip();
public abstract void setHomePhone(String phone);
public abstract String getHomePhone();
// 工作地址信息
public abstract void setWorkPhone(String phone);
public abstract String getWorkPhone();
public abstract void setEMail(String email);
public abstract String getEMail();
...
}
从属对象随实体 bean 的存在而存在,随实体 bean 的中止而中止,这是理解从属对象与实体 bean 之间关系的关键。从属对象包含在一个具体的实体中,所以删除这个实体将导致从属对象也被删除。用关系数据库的术语来说,有时这就称为级联删除。
从属对象,如 ContactInfo,用在关系字段中。与实体 bean 形成关系的从属对象技术上称为从属对象类。EJB 客户端应用程序永远不能直接访问从属对象类;这种类不能用作 bean 的远程或本地接口中的参数或返回值。从属对象类只对 bean 类才是可见的。
从属对象类不适合作为远程参数类型,因为它们与 bean 在运行时的持久性逻辑有密切的联系。持久性管理器扩展了抽象的从属对象类,以便能提供一种实现,可用于在运行时管理 bean 的持久性状态。此外,抽象的持久性方案还为数据建模 -- 而不是为那些由企业级 bean 表示的业务概念建模 -- 所以,作为一种设计策略,将抽象的持久性方案对 EJB 客户机隐藏起来是有意义的。
例如,ContactInfo 关系字段中除了 bean 的客户机所需的简单地址信息之外,还包含许多其它信息。虽然您可以使用抽象持久性方案中的从属对象类 ContactInfo(它对 bean 的客户机是隐藏的),但是,您得用其它的对象来把这些数据实际表露给客户机。下面是一个示例,说明了如何对 EJB 客户机隐藏 ContactInfo 从属对象。在此例中,地址信息是通过在 EJB 1.1 的示例中开发的 Address 对象来表露的。
// Employee bean 的远程接口
public interface Employee extends javax.ejb.EJBObject {
public Address getHomeAddress();
public void setHomeAddress(Address address);
public int getIdentity() throws RemoteException;
public void setFirstName(String firstName) throws RemoteException;
public String getFirstName()throws RemoteException;
public void setLastName(String lastName) throws RemoteException;
public String getLastName() throws RemoteException;
}
// Employee bean 的 bean 类
public abstract EmployeeBean implements
javax.ejb.EntityBean {
...
public Address getHomeAddress(){
ContactInfo info = getContactInfo();
Address addr = new Address();
addr.street = info.getStreet();
addr.city = info.getCity();
addr.state = info.getState();
addr.zip = info.getZip();
return addr;
}
public void setHomeAddress(Address addr){
ContactInfo info = getContactInfo();
info.setStreet(addr.street);
info.getCity(addr.city);
info.getState(addr.state);
info.getZip(addr.zip);
}
....
// 容器管理的关系字段
public abstract void setContactInfo(ContactInfo info);
public abstract ContactInfo getContactInfo();
...
}
尽管容器管理的关系字段没有表露给客户机,但您仍然可以从远程接口直接使用容器管理的持久性字段。请注意,用来访问 firstName 和 lastName 的容器管理的持久性字段是在远程接口中使用的。
一个 bean 与各种从属对象类之间可能具有多种不同的关系,它们由这种关系的对应性和方向来定义。Bean 与从属对象类之间可以有一对多和一对一的关系。例如,Employee bean 可能仅有一个 Benefit 从属对象类,但可能有许多 ContactInfo 从属对象类。
public abstract EmployeeBean implements
javax.ejb.EntityBean {
...
public abstract void setContactInfos(Collection addresses);
public abstract Collection getContactInfos():
public abstract void setBenefit(Benefit benefit);
public abstract Benefit getBenefit();
...
}
与从属对象类的一对多关系既可表示为 java.util.Collection 类型,也可表示为 ava.util.Set 类型(注:在本规范的后续版本中,java.util.Map 和 java.util.List 被视为附加的返回类型),而与从属对象的一对一关系则使用从属对象的类型。
实体 bean 也可以定义与其它实体 bean 的关系。这些关系可以是一对一、一对多或多对多。例如,Employee bean 可能有许多子级 bean,而只有一个配对的 bean。下面的代码段使用抽象持久性方案的方法习语,说明了如何为这些关系建模。该应用程序中,子级 bean 和配对的 bean 都表现为 Person bean。
public abstract EmployeeBean implements
javax.ejb.EntityBean {
...
public abstract void setSpouse(Person manager);
public abstract Person getSpouse();
public abstract void setChildren(Collection family);
public abstract Collection getChildren();
...
}
与另一个 bean 的一对多关系表示为 java.util.Collection 类型或 java.util.Set 类型,而一对一关系则使用该 bean 的远程接口类型。
从属对象本身与同一个 bean 中的其它从属对象之间可以有一对一、一对多和多对多的关系。此外,从属对象与其它实体 bean(除其父级 bean 之外)也可以有一对一、一对多的关系。下面的示例显示,Benefit 从属对象类与 Salary 从属对象(一种报酬计算程序)之间怎样具有一对一的关系,而与 Investment bean 又怎样具有一对多的关系。
public abstract class Benefit {
public abstract void setSalary(Salary salary);
public abstract Salary getSalary();
public abstract void setInvestments(Collection investments);
public abstract Collection getInvestments();
}
在部署时,部署者将使用持久性管理器工具来具体实现这个 bean 类及其从属类。这些具体实现将在运行时保持各种关系,并使各 bean 实例的状态与数据库同步。容器将在运行时管理持久性实例,从而提供一种强健的环境,其中具有自动的访问控制和事务控制。
bean 也可以定义从属对象的值,这些对象是可序列化的对象,如 EJB 1.1 示例中的 Address 对象。这些值通过序列化而变为持久的,它们并不形成与 bean 的关系 -- 它们是严格的容器管理的持久性字段。
容器与持久性管理器之间也已经定义了一个合约,使持久性管理器可以获得事务的句柄,并访问由该容器管理的数据库连接池。这个合约稍嫌宽松,将来还需要使其更为严格,但它是允许持久性管理器跨 EJB 容器移植的基础。容器和持久性管理器之间合约的细节已超出了本文的范围。
除了通过抽象持久性方案定义持久性之外,EJB 2.0 还提供了一种新的查询语言,用来说明持久性管理器应该如何实现 CMP 中的各种查找方法。