有效的实体 EJB 开发
随着越来越多的程序员在其项目中使用 Enterprise JavaBean (EJB) 组件,对简化 EJB 开发的工具需求也日渐增长。本文讨论容器管理的持久性 (CMP) 实体 Enterprise JavaBean,并介绍用于创建这种 JavaBean 的免费工具。
容器管理的持久性 (CMP) 实体 Enterprise JavaBean 本质上是持久数据的封装器(通常以关系数据库表形式),另外还支持事务控制和安全性。CMP 实体 bean 至少从六个单独文件构造,并有在编译期间无法方便验证的相关性。必须注意确保对象到关系数据库映射的正确性,理想情况下,应该将代码设计成在需求变更时所 需的更改最小。本文描述在编写大型 EJB 项目时面临的某些挑战,以及如何通过可从 IBM alphaWorks Web 站点(请参阅 参考资料)下载的 EJBMaker 工具克服这些问题。
EJBMaker 读取一个 CMP EJB 描述文件(用 XML 编写),然后自动生成 Java 源代码和数据库脚本。该工具负责 bean 的 finder 和初始化方法的烦琐定义,并能够生成使 CMP EJB 相互关联的关系 EJB。EJBMaker 跟踪 XML bean 描述符文件中的更新,并只对已更改的 EJB 重新生成 Java 代码。另外还生成 adaptor 类以允许快速初始化以及从使用 XML 的 EJB 抽取数据。EJBMaker 与 IBM WebSphere 应用服务器一起使用时性能最优,但经过一些手工调整,也可以与其它应用服务器一起使用。
EJB 技术的优势之一在于(至少在理论上),EJB 只编写一次就可以在任何遵循 EJB 的应用服务器上运行。规范的 2.0 版包含了如 EJB 查询语言等的技术,带领我们朝这个目标更进一步。
虽 然还没有实现“编写一次,到处运行”的承诺,但是 EJB 可以十分方便地从一个应用服务器环境移植到另一个 -- 特别是容器管理的持久性 (CMP) 实体 EJB 更是如此。在编写 CMP 实体 EJB 时,开发人员不必担心有关数据库中持久存储 bean 状态的编码。在部署阶段,所有这些代码都在应用服务器上自动生成。这会使 bean 的灵活性有所降低,但另一方面,却可以进行更快速的开发。应用服务器设计者可以自由使用任何设计成持久存储和检索 EJB 数据的方法,只要这些方法遵守“容器管理的持久性实体 bean 组件契约”即可,由 EJB 规范定义(请参阅 参考资料)。目前为止,最常见的解决方案是使用 Java 数据库连接 API (JDBC) 将数据存储在关系数据库中(有关 JDBC 的信息,请参阅 参考资料)。这是 IBM WebSphere 应用服务器和 BEA WebLogic(以及其它服务器)使用的技术。
另 一种实体 EJB 类型是 Bean 管理的持久性 (BMP) EJB。在这种情况下,开发人员负责手工编写代码来持久地检索或存储 bean 信息。当需要将数据存储在旧有存储系统或文件系统中时,BMP 是理想的。然而,灵活性增加是以可移植性降低为代价的。当应用服务器不在控制所有对系统资源的访问时,无法保证这些资源在不同服务器环境中的存在和兼容 性。
![]() ![]() |
![]()
|
除非确实有强制 BMP 实现的特定需求,否则 CMP 是更好的选择。如何编写 CMP bean 呢?需要创建以下文件:
- EJB 远程接口。该文件包含任何访问或修改存储在 bean 中数据的方法的方法签名。远程接口必须扩展 javax.ejb.EJBObject接口。
- EJB 远程实现。该文件提供在远程接口中定义的所有方法以及应用服务器所需的方法(例如回调方法)的实现。要确保所有需要的方法都存在,EJB 远程类必须实现 javax.ejb.EntityBean。
- EJB 宿主接口。该宿主接口声明任何创建 bean 新实例的方法以及所有用来检索 bean 实例的方法( finder方法)的方法签名。EJB 宿主接口扩展 javax.ejb.EJBHome接口。
- EJB 键。该键类包含 bean 的唯一主键实现。EJB 规范的 2.0 版将主键类定义成任何是 RMI-IIOP 中合法值类型的类。
- EJB finder helper 接口。该文件为每个在 EJB 宿主接口中声明的 finder 方法包含一个静态 java.lang.String 字段。这些字符串用 SQL 查询进行初始化,当在 finder 方法中检索 bean 实例时,这些 SQL 查询动态执行。
注:该文件特定于 IBM WebSphere 应用服务器。其它应用服务器环境(例如 Enhydra 或 WebLogic)使用专用查询语言格式将查询放在 XML 部署描述符中。在这些环境中,可以忽略该文件并通过手工将查询输入部署描述符来替代。 - 部署描述符。 部署描述符是一个序列化的 Java 类,它包含有关 bean 的元数据,例如 EJB 类和接口的名称,以及与 bean 相关的一个持久字段列表。从 EJB 规范 1.1 版开始,可以用 XML 编写部署描述符(随后在部署阶段转换成序列化的 Java 对象)。
- 数据库脚本。如果应用服务器没有创建持久存储 bean 数据所需的关系数据库表,可以创建用于此目的的数据库脚本。
![]() ![]() |
![]()
|
除了确保所有上述文件都存在之外,开发人员在编写 CMP 实体 bean 时还要避免几个缺陷。首先,文件之间存在相关性,但无法在编译期间进行验证。
- 必须注意要确保在 EJB 远程实现类中实际实现了远程接口中声明的方法。如果 EJB 远程实现类要直接实现 EJB 远程接口,则可以在编译期间检查相关性的验证。然而,远程接口与远程实现类的实例有内在不同,前者是客户机方对象,而后者则属于服务器方。如果远程实现类 要实现远程接口,它还必须实现由远程接口 ( javax.ejb.EJBObject ) 扩展的接口所声明的方法。该接口包含很多不属于服务器方的方法。
- 在 bean 的 EJBHome 接口中声明的 finder 方法必须与一个查询相关联,查询通常在另一个文件中定义。在 WebSphere 中,对于 EJB 宿主接口中声明的每个 finder 方法,EJB finder helper 类必须包含一个静态 String 字段,并且该字段的名称必须与 finder 方法的名称一致。Enhydra 要求 XML 部署描述符包含与在 EJBHome 等中声明的 finder 方法一致的 finder-方法-jdbc-映射元素。
- 对于 EJB 宿主接口中声明的每个 create(...) 方法,EJB 远程实现类必须包含一个 ejbCreate(...) 方法。特别是,它必须总包含 ejbCreate(EJBKey...) 方法,该方法使用 EJB 键实例创建一个新的 bean 实例。
其它需求包括:
- 确保 XML 部署描述符包含正确的 EJB 组件类名和所有持久字段所需的声明。
- 正确设置对象到关系数据库的映射。这包括确保将持久字段映射到正确类型的数据库列,以及将表设计成满足存在于应用服务器自动生成的持久代码中的任何特殊需求。列顺序可能就是这样一个需求。
看 过所有这些需求之后您可能会想,编写 CMP 实体 bean 可能不适合于意志薄弱者。幸运的是,有一些工具将辅助您完成这些步骤中的很多步。例如,IBM VisualAge for Java IDE 包含一个极好的 EJB 开发环境,该环境将为您自动生成许多必需的代码,甚至可以帮助您进行对象到关系数据库的映射。BEA Systems 的 WebGain Studio和 WebGain StructureBuilder通过自动将 UML 模型转换成 EJB 组件等来帮助开发人员处理某些更棘手的 EJB 开发任务。但是如果不能自由切换 IDE 该怎么办呢?那种情况会很麻烦,除非...
![]() ![]() |
![]()
|
为 帮助解决某些与 CMP bean 相关的棘手问题,创建了 EJBMaker 工具。虽然该工具生成的是特定于 WebSphere 的文件,但是修改用于其它环境的输出却相当简便。所有与 CMP bean 相关的信息(包括 bean 名称、持久字段名称和类以及关系数据库映射)都存储在一个文件中。该文件的格式是很灵活并易于为 EJBMaker 未来版本扩展的 XML。
从该 EJBMaker XML 文件生成以下内容(请参考前面的 “CMP bean 剖析”):
- 每个 CMP bean 的 Java 源文件. 这包括 EJB 远程接口、EJB 远程实现类、EJB 宿主接口、EJB 键类和 EJB finder helper 接口。
- XML 部署描述符.包含 EJB 类名称、事务的持久字段和 bean 属性、安全性和隔离级别。
将自动照管所有相关性,包括对象到关系数据库映射。生成缺省的 finder 方法来检索与 bean 中每个持久字段值匹配的实例,并检索所有现有的 bean 实例( getAllInstances() 方法)。这确保 bean 足够灵活以在很多不同方案中使用。如果需要特殊的 finder 方法,可以直接在 XML 描述文件中输入。
下面是以 EJBMaker XML 格式描述的一个 CMP bean 示例:
<?xml version="1.0"?> |
该文件有几部分。 transaction-attr 、 isolation-level 、 run-as-mode 和 re-entrant 直接取自 Sun 的 XML EJB 部署描述符格式。这些 bean 属性作为所有 bean 的缺省属性复制到生成的部署描述符。
在那以后遇到的第一个元素是:
这是新 bean 声明的开始。该 bean 的 Java 命名与目录接口 (JNDI) 名称是 'Account',类型是 'entity'。EJBMaker 目前只支持 'entity' bean 类型。
下几行指明如何标识持久字段:
|
在 Account bean 中定义的第一个持久字段是 ACCTNUMBER。用于存储该持久字段的 Java 类是原始的 int 类(由 dt 属性声明),用作为 INTEGER 的 col_dt 属性声明该数据库列的数据类型。还多定义两个持久字段 SSN 和 BALANCE。
下几行指明如何指定特殊 finder 方法:
|
finder 方法的名称是 findNegativeAcct。请注意,位于元素中的 SQL 代码必须符合 XML 内容规则。特别地,这意味着必须避免如小于号等保留 XML 字符。
向 EJBMaker 传递 XML 文件将生成源代码、XML 部署描述符和数据库脚本。
![]() ![]() |
![]()
|
从上例可生成以下 Java 类和方法:
Account.javaMethods: getACCTNUMBER(), setACCTNUMBER(int), getSSN(), setSSN(String), getBalance(), setBalance(double), getKey(), initialize(TXDocument), getXML() AccountBean.java
Methods: getACCTNUMBER(), setACCTNUMBER(int), getSSN(), setSSN(String), getBalance(), setBalance(double), getKey(), initialize(TXDocument), getXML(), ejbActivate(), ejbLoad(), ejbPassivate(), ejbStore(), ejbRemove(), setEntityContext(), unsetEntityContext() AccountHome.java
Methods: create(AccountKey), findByPrimaryKey(AccountKey), findAllInstances(), findByACCTNUMBER(int), findBySSN(String), findByBALANCE(double), findNegativeAcct() AccountKey.java
Methods: AccountKey(), AccountKey(String), equals(Object) AccountBeanFinderHelper
No methods AccountXMLAdaptor
Methods: AccountXMLAdaptor(), AccountXMLAdaptor(TXDocument), getXML(), setACCTNUMBER(Integer), getACCTNUMBER(), setSSN(String), getSSN(), setBALANCE(Double), getBALANCE()
某些方法需要进一步解释。远程接口 ( Account.java ) 和远程接口实现 ( AccountBean.java ) 有一个名为 getKey() 的方法。该方法只返回 bean 的主键作为 String 实例,而不是由其超类的 getPrimaryKey() 方法返回作为 AccountKey 实例。
远程接口和远程实现类还有 initialize(TXDocument) 和 getXML() 方法。这两个方法用来使用 XML 快速初始化 bean,以及抽取以 XML 形式存储在 bean 中的信息。有关如何使用这两个方法的信息,请查看此 示例。
这段代码还解释了如何使用 AccountXMLAdaptor 类。它是 Account XML 文档的封装器,并包含设置与获取与 Account 相关特性的方法。请注意,原始 Java 类的持久字段被转换成 adaptor 中对等的非原始类。按这种方式,就可以使用空值来表示 未初始化 状态,这对只设置持久字段的子集很有用。使用 adaptor 类的 getXML() 方法以 XML 形式编码所有与 Account 相关的信息。XML 格式很简单,根元素总是 EJB 名称, 对于每个持久字段包括一个元素。在 Account 示例中,可以将 EJB 实例序列化成以下 XML 文档:
|
![]() ![]() |
![]()
|
EJBMaker 还可以生成用来表示 bean 之间多对多关系的 EJB。例如,考虑一个简单的日程应用。使用两个 CMP 实体 bean,一个包含有关日程活动(称为 Event)的信息,另一个包含有关日程应用客户(称为 Client 的信息)。现在要表示参加特定日程活动的客户。一种原始的解决方案是向 Event bean 添加一个包含所有参加特定活动的客户主键的字段。可以使用分隔符(例如逗号)来并置主键。要检索所有参加活动的客户,必须编写代码来接受用逗号分隔的主键 字符串,并使用 Client 接口中的 findByPrimaryKey(...) 对每个键进行查询。即使在会话 bean 中编写代码,与第二种解决方案相比性能是缓慢的。
更好的解决方案是使用另一个表来表示事件和客户之间的关系。两个列包含 Event EJB 实例和 Client EJB 实例的主键。现在,检索所有参与活动的客户的任务只是在 Client 宿主接口中编写 finder 方法和在 helper finder 类中编写 SQL 查询。这正是 EJBMaker 要做的。使用以下技术构造两个 bean 之间的关系:
|
用 名为 Attendee 的新 CMP 实体 bean 表示关系,该 bean 有两个字段(如果加上主键字段则有三个)。这两个字段包含 Event EJB 和 Client EJB 的主键实例。另外,还创建了两个附加的 finder 方法,一个位于 Event 宿主接口,另一个位于 Client 宿主接口。Event 宿主接口 finder 方法是:
java.util.Enumeration findByAttendee(String pkey) |
'pkey' 是 Client EJB 的主键。该方法返回特定客户参加的活动的枚举列表。客户宿主接口有同样的方法:
java.util.Enumeration findByAttendee(String pkey) |
然而在该类中,'pkey' 是 Event EJB 的主键,该方法返回参加特定活动的客户的枚举列表。
![]() ![]() |
![]()
|
EJBMaker 生成的代码的最后一部分是用来创建持久 EJB 表的数据库脚本。除了创建所有必需表的 CreateTables.ddl 脚本之外,还要为每个 EJB 创建一个脚本。然而,在创建虚表(或视图)之前,应用服务器不能使用表。视图改变表中的列顺序,以便这些列与生成的 EJB 持久性代码所期望的顺序相对应。EJBMaker 中有一个工具可以为您生成 CreateViews.ddl 脚本。必须在每次部署 EJB 时生成该脚本,否则列顺序可能会出错。通过在应用服务器和物理数据库表之间使用视图抽象,即使重新部署 EJB,也可以使信息存储在持久性表中。
来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/374079/viewspace-130483/,如需转载,请注明出处,否则将追究法律责任。
转载于:http://blog.itpub.net/374079/viewspace-130483/