ITPub博客

首页 > Linux操作系统 > Linux操作系统 > [转载]用 VisualAge for Java 实现 EJB 的 OO 设计

[转载]用 VisualAge for Java 实现 EJB 的 OO 设计

原创 Linux操作系统 作者:dinner1007 时间:2019-01-28 11:21:06 0 删除 编辑

用 VisualAge for Java 实现 EJB 的 OO 设计


重温 EJB

还记得 EJB 规范版本 1.0 中描述的吗:VisualAge for Java Enterprise Update EJB Development Environment 支持两种类型 Enterprise JavaBean 的开发:

  • 表示活动对象的会话 bean,它不是“属于”单客户机(“有状态”会话 bean),就是完全可计算的,没有持久状态,以及在多个客户机之间共享(“无状态”会话 bean)
  • 表示共享商业对象的实体 bean,它以持久存储机制(如关系数据库)存储,其存活期超过一个用户会话。有两种实体 Bean 类型:“容器管理的”的 bean,其持久性由 EJB 服务器本身管理;以及“bean 管理的”bean,其持久性必须由 bean 开发人员提供的代码处理。

VisualAge for Java(扩展到 WebSphere)提供一种将实体 Bean 映射 DB2 的简单机制。这种简单性来源于以下假设:EJB 中的每个属性都映射到数据库某表中的某列。这种映射根据类型进行 -- Java 中的 String 映射成 SQL 中的 VARCHAR,int 映射成 INTEGER,以此类推。VisualAge for Java 还允许 bean 开发人员以某些简单方式定制这种映射。例如,可以将诸如 "name" 这样的 "Complex" 属性映射成两列:"firstname" 和 "lastname"。

但是,有一条映射规则却与众不同。对于不能转换成标准 SQL 类型的对象,将其映射成 BLOB(二进制巨对象),最大长度为 1 MB。这条规则适用于任何从 Java 中 Object 派生的类型 -- 也就是说,您可能创建的每个类。

对于在关系数据库中存储,这是一种简单的解决方案,但是对于大多数应用程序,并不是最佳方案。在关系数据库中表示对象数据的方法通常有两种。一种方法是前面提到的 "BLOB" 方法,其中将对象序列化成二进制形式,并存储在表的某一列中。这种方法有以下缺陷:

  1. 其它应用程序(如报表工具)无法读取 BLOB
  2. 无法通过 SQL 查询 BLOB,使数据挖掘和表维护难以进行
  3. 创建 BLOB 应用程序的后续版本可能无法读取 BLOB。这是个棘手的问题,只有深入了解 Java 序列化的工作原理并制定周密计划才能解决。

由于这些缺陷,开发人员通常将对象映射成关系数据库,以便在关系数据库模式中保持对象关系。有关该方法的解释,请参阅 Crossing Chasms: A Pattern Language for Object-RDBMS Integration。现在很快回顾一下此方法,来看看如何将它应用到 VisualAge for Java 中的 EJB。




回页首


对象-关系映射与设计基础

在最普遍映射方法中,对象关系由数据库中的外键关系表示。例如,假设有以下关系:



在 Java 中,用名为 Resume 的 Java 类表示这个关系,该类有一个类型为 Address 的实例变量。在运行期间,每个 Resume 实例都包含一个 Address 类的实例。如果仔细观察这个例子,就会发现还要表示另一种关系。Resume 可能还包含一工作历史,(在 Java 中),可能将其表示成 Job 的一个集合。因此,要表示以下关系:



(例如)可以向类型为 java.util.Vector 的 Resume 类中添加一个实例变量。在运行期间,Vector 将包含 Job 的实例。因此,可按如下定义一个非常简单的类:

import java.util.*;
/** * This class represents a Resume in our Jobs site */
public class Resume {
public Address homeAddress;
public Vector jobHistory;
}

到目前为止,还没有什么新东西。我们都知道如何在 Java 中表示对象,否则我们就不是 Java 程序员了。问题在于,这种 Java 设计如何映射到关系数据库?以及那又怎样帮助我们理解如何在 EJB 中实现我们的设计?马上就有答案 -- 耐心一点,下一部分的讨论将回答所有问题。

让我们看一下同一个对象设计是如何在关系数据库中表示的。如 [Brown96] 所述,如前一例的一对一关系 (Resume 对 Address) 在关系数据库中由外键表示,外键从“所有者”表指向“被拥有者”表。下列表演示了这种方法:




回页首


Resume 表

PrimaryKeyNameAddressFK
1000Bob Smith2013



回页首


Address 表

PrimaryKeyStreetCity
2013203 Maple Ln.Raleigh

Resume 表中的 AddressFK 列中包含一个指向 Address 表的外键 -- 每个 Resume 行中都有一个实际上指向 Address 的指针。一对多关系同样用外键存储,但外键指向 相反方向。每一个“被包含”行都有一个外键指向“包含”它的行。下表演示此过程:




回页首


Job 表

PrimaryKeyResumeFKJobTitle
10111000Senior Programmer
10121000Programmer/Analyst

在此示例中,显示的两个 Job 行都有一个外键指回 Resume 表,由于有了外键,两个 Job(1011 和 1012)可以指回到前面的主键为 1000 的 Resume。现在可以了解解决方案的基本轮廓了。如果要将 Java 中的对象模型映射到关系数据库,必须通过某种方式创建并重组这些外键关系。简而言之,这就是 EJB 中特殊用途代码要做的事。




回页首


EJB 关系映射

这样看来,需要的是两方面的最佳组合。我们不但要象在标准 Java 类中那样表示对象关系,而且还想利用 EJB 中自动持久性特性。下面看一下 Resume 示例是如何通过 EJB 实现的,就会知道怎样兼顾二者。最终将创建三个 EJB:一个表示 Resume,一个表示 Address,一个表示 Job。决窍在于:如何将这三个 EJB 结合在一起。

首先要看一下的是 Resume EJB 的远程接口。如下所示:

package com.ibm.ejbs.examples;
/*** This is an Enterprise Java Bean Remote Interface*/
public interface Resume extends javax.ejb.EJBObject {
void addJob(String title, String employer, Date hiredate, Date terminationDate)
throws java.rmi.RemoteException;
Address getAddress() throws java.rmi.RemoteException;
java.util.Enumeration getJobs() throws java.rmi.RemoteException;
String getName() throws java.rmi.RemoteException;
void setName(String name) throws java.rmi.RemoteException;
}

关于这个接口,有两点要指出。第一,getAddress() 方法返回 Address 实例。第二,getJobs() 方法返回枚举值(在本示例中,返回 Jobs 的枚举值)。理解如何实现这两个方法,是理解这种 EJB 关系管理方法的关键所在。远程接口方法说明了: 可以 得到相关的 Address 和 Job 的枚举项,而不说明 如何 做到。“如何做”方法由外键处理,下面就将看到。但是,在这之前,先看一下如何创建这个特别的 EJB。

用 VisualAge for Java “创建 EJB” 智能向导生成这个 EJB。使用这个智能向导创建一个容器管理的实体 Bean "Resume"。智能向导自动创建一个 primaryKey 实例变量,并生成一个主键类 "ResumeKey"。然后使用“创建域”智能向导,并选中“生成读写方法”复选框,来添加 "name" 字段。利用它的读方法和写方法方法,使用同一个智能向导来创建另一个字段 "addressFK"。然后使用“添加到 - 远程接口”菜单,将 getName() 和 setName() 方法提升为 Bean 接口。最后,使用“创建方法”智能向导,添加 getAddress() 和 getJobs() 方法,并将它们添加到远程接口。

创建 Address 和 Job EJB 的方法大体相同。使用“添加 EJB”智能向导,创建一个 CMP 实体 EJB "Address" ,然后添加 street、city、state 和 zip 字段。然后,将这些字段的读方法和写方法提升为远程接口,并添加 ejbCreate() 方法,该方法允许立即设置所有属性。最后,创建带有 "jobTitle" 字段和 "resumeFK" 附加字段的 Job EJB。

我们已经看到如何创建 EJB,现在来看一下如何实现上面提到的 getAddress() 和 getJobs() 方法。首先看一下 getAddress() 代码,因为它最简单,并且不涉及太多额外 EJB 代码。

public Address getAddress() { 
Address address = null;
if (addressHome == null) {
if (initContext == null)
obtainInitialContext();
obtainAddressHome();
}
try {
AddressKey key = new AddressKey(getAddressFK());
address = addressHome.findByPrimaryKey(key);
} catch (Exception e) {
System.out.println("+++Exception caught:" + e);
} return address;
}

现在预演一下这个方法,然后了解它的工作原理。代码中 "try" 块之前的开始部分只对 addressHome 和 initialContext 进行简单初始化。obtainInitialContext() 方法通过创建 InitialContext 的新实例,从一组标准 Properties 获得初始环境。这个代码是标准样本,这里不再讨论。obtainAddressHome() 也是标准代码 -- 它使用 initalContext 来获得对 AddressHome 的引用,AddressHome 存储在 addressHome 实例变量中。本示例唯一不寻常之处在于:这个代码位于 EJB 中,而不在 EJB 客户机中(象大多数 WebSphere 示例演示的那样)。这是关键所在 -- Resume EJB 既是客户机, 同时 也是服务器 -- 它将作为 Address 和 Job EJB 的客户机。

代码的下一部分(try 块中的代码)将外键索引转换成对象。其实很简单 -- 首先使用 Resume 中已有的地址外键值,创建 AddressKey 的一个新实例。然后用 AddressHome 实例查询那个键的 Address。这由 findByPrimaryKey() 方法完成,findByPrimaryKey() 方法在所有 EJB Home 中自动定义。然后,返回 Address 实例。

getJobs() 方法与之非常类似。现在看一下它的代码:

public java.util.Enumeration getJobs() { 
java.util.Enumeration enum = null;
if (jobsHome == null) {
if (initContext == null)
obtainInitialContext();
obtainJobsHome();
} try {
enum = jobsHome.findByResumeFK(primaryKey);
} catch (Exception e) {
System.out.println("+++Exception caught:" + e);
}
return enum;
}

和前一个方法一样,代码的开始部分对 jobsHome 中的 InitialContext 和 JobHome 实例进行简单初始化。try 块中的代码更为有趣。通过向 JobHome 发送消息 findByPersonFK(),来获得这个方法所返回的枚举值。传给这个方法的自变量是 Resume 中的 primaryKey。该方法在 JobHome 中定义,如下所示:

public interface JobHome extends javax.ejb.EJBHome { 
public Job create (JobKey primaryKey) throws javax.ejb.CreateException, java.rmi.RemoteException;
Job create(JobKey arg1, String arg2, int arg3, String arg4) throws javax.ejb.CreateException, java.rmi.RemoteException;
java.util.Enumeration findByResumeFK(String resumeFK) throws java.rmi.RemoteException, javax.ejb.FinderException;
public Job findByPrimaryKey (JobKey primaryKey) throws java.rmi.RemoteException, javax.ejb.FinderException;
}

还记得一对多关系的表示方法吗:由多行指回包含这些行的对象所在的那行。JobHome 通过执行以下 SQL 代码来实现该方法,在 JobBeanFinderHelper 类中定义:

public interface JobBeanFinderHelper {
public static final String findByResumeFKQueryString = "select * from userid.RelJobBeanTbl where resumeFK = ?";
}

这段 SQL 代码返回一组行,这些行表示了有一个与作为参数传递的 Resume 主键对应的 ResumeFK 的那些 Job。这就是要与 Resume 关联那组对象,这样,我们的任务也就完成。




回页首


其它考虑事项

虽然本文概述了 EJB 中关系管理解决方案的基础,但是并没有解决这种方法的所有问题。还有其它问题需要解决。例如,没有解决创建对象之间的关联问题,例如,将特定 Address 实例与 Resume 相关联。本文只讲述了问题的“读方法”部分,相应的“写方法”部分涉及到从 Address 实例中取出主键,然后在 Resume EJB 中设置外键。

另一个关键问题是:对于商业逻辑范畴之外的唯一键,如何管理它们的生成。可以将 Address 表中的所有列作为 Address 表的主键,但这样不是特别有效。或者,可以创建一组 Generator 类(同样也可以是 EJB),来处理每一个新 Address 实例中主键的生成。这个问题本身就很有趣,有可能成为 VisualAge for Java 中有关 EJB 的另一篇文章的好题目。

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/374079/viewspace-130479/,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论

注册时间:2018-08-23

  • 博文量
    192
  • 访问量
    92836