本文简要地介绍了Java 2 Enterprise Environment(J2EE)环境,并讨论了 J2EE开发中使用的面向对象方法(分析、设计、实现),以及一些与对象持久性相关的问题。
WebSphere 越来越受欢迎说明 J2EE 环境已经越来越普遍地用于部署商业应用程序。技术人员都必须熟悉该环境。本文简要地介绍了 J2EE 环境,并讨论了 J2EE 开发中使用的面向对象的方法(分析、设计、实现),以及一些与对象持久性相关的问题,因为对象持久性与对象-关系数据库管理系统(ORDBMS)的使用相关联。
简介
数据库的选型应该是会为您带来商业优势的战略性决策。一旦做出决策,您就必须充分利用该数据库服务器来实现这一优势。本文简要地介绍了Java 2 Enterprise Environment(J2EE)环境,并讨论了 J2EE开发中使用的面向对象方法(分析、设计、实现),以及一些与对象持久性相关的问题,因为对象持久性与对象-关系数据库管理系统(ORDBMS)的使用相关联。
Web 体系结构背景
J2EE 包含几十个缩写词,每个均代表不同的概念。要理解这一复杂性,回顾一下 Web 体系结构的发展过程是十分有用的,如图1 所示。 (图片)
图 1. Web 体系结构 图 1 中所展示的体系结构代表了 1996 年的 Web 环境。那时,浏览器和 Web 服务器主要是由 Netscape 提供的,而Netscape 的产品在当时较之以前的产品来说是一个重大的进步。
在图 1 的左侧,我们看到的是浏览器。它包含一些附加功能,以使用插件来提供像显示 .pdf 文件这样的功能。它还增加了一些编程功能,以改善与用户的交互。其中包括一种能够添加到 HTML 语言中的脚本语言JavaScript(独立于 Java 创建的),以及可包含在 HTML 页中进行下载的 Java applet。
该浏览器使用超文本传输协议(HTTP)与 Web 服务器进行通信。HTTP 的重要特点就是它建立在请求-响应模型的基础上。每个请求-响应都是相互独立的。Web 服务器不会等待当前客户机的下一请求。因此,该协议是无状态的(stateless)。
万维网(WWW)是为了易于访问文档而创建的。因为每个请求都是单独完成的,所以无需追踪复杂的交互。Web 服务器可以接收请求,并使用所提供的信息在由 Web 服务器控制的目录结构中检索所请求的文档。
在发展初期,Web 服务器的定义中添加了一个称作公共网关接口(Common Gateway Interface,CGI)的接口。这是一种按照所定义的协议来调用以指定格式接收信息的程序,使用该程序来实现请求,并将之返回给 Web 服务器的简单方式。该方式还有能力调用一个可以生成更多动态内容的程序。因为这是对于浏览器请求的扩展,所以 CGI 程序是暂时性的:通过请求创建,一旦返回结果就终止。
CGI 协议也有其局限性,并用于实现访问关系数据库的应用程序。这意味着每当向驻留在关系数据库中的信息发出请求时,都必须打开数据库连接,插入、更新和检索数据,以及关闭连接。在大多数情况下,多数时间都消耗在数据库的连接上。
人们想出了两个办法来解决该问题:
让 CGI 程序与永久性程序进行对话,以及通过包含 API 来扩展 Web 服务器的功能。
第一个办法是让 CGI 程序与永久性程序进行对话,这个方法可以有许多变种。CGI 程序可以启动一个要基于某个 ID 来进行访问的应用程序,而这个 ID 是我们在响应中返回的(如图 1 中的 app-srv 所示)。如果此 ID 在某个间隔内还没有被重新使用,程序将终止。另一种方式是让一个永久性程序来处理所有客户机向该应用程序发送的请求。该场景极可能需要一个多线程程序,用于验证客户机的有效性,以及向每个新的客户机分配ID。然后,它必须记录每个客户机 ID 的超时时间。
第二个办法是通过包含 API 来扩展 Web 服务器的功能,即利用 Web 服务器 API(图 1 中的NSAPI)来编写应用程序,并将之驻留在 Web 服务器上。通过该方法,Web 服务器可以保持数据库连接,以及包含应用程序特定的处理信息。这种方法也需要记录用户和连接超时。
进入应用程序服务器将各种 Web 应用程序的实现方式合并成为一个更完整的架构是有道理的。那时,Java 倡导的“一次编写,到处运行”的概念已经十分流行。所以,J2EE 的成型并未花很长时间。
J2EE 是一个 Java 规范,且深受面向对象(OO)程序设计方法的影响(Java 是一种 OO 编程语言)。其目标是提供一个应用架构,其中包含实现企业应用程序所需的所有特性,这包括可移植性、可伸缩性、事务控制,等等。J2EE 规范包括:
J2SE:Java 2 Standard Edition 包含了著名的 Java 环境,其中包括与平台无关的 Java开发工具箱/Java 运行时环境(JDK/JRE)、多线程环境、Java 基础类等。
EJB:Enterprise Java Beans 提供了在分布式环境中表示对象的标准方式。EJB 具有三种类型:会话bean、实体 bean 和消息 bean。
Servlets:Java Servlets 提供了请求-响应通信模式中的操作机制。
JSP:Java Server Pages 是特殊类型的 servlets,用于动态创建 HTML 页并显示给用户。
JDBC:Java 数据库连接接口提供了与诸如关系数据库的数据源进行通信的标准化方式。
JTA/JTS:Java 事务 API 与 Java 事务服务。
JMS:Java 消息服务。
JNDI:Java 命名和目录接口。该接口对于 J2EE 环境至关重要,因为它提供了一种无需知道其位置就可以追踪资源的方式。您可以将之与 LDAP 目录服务等同起来。
JavaMail
JAXP:用于 XML 处理的 Java API。它还包括用于 XML 注册的 Java API(JAXR)和基于 XML 的 RPC 通信协议(JAX-RPC)。
Connector Architecture:该体系结构提供了与未集成在 J2EE 环境中的遗留系统进行通信的方式。
JAAS:Java 认证和授权服务。
这些规范仍然在不断演变着,而且还在添加更多组件。颇有意义的是,这些规范是基于标准的,并且致力于提供应用程序提供者、应用程序服务器提供者以及硬件平台之间的可移植性。正如您可以看到的,J2EE 环境正试图提供企业应用程序中所需的所有可能服务。这包括了多年以来以不同形式提供的许多服务。
图 2 中提供了 J2EE 环境的高层表示。而真正的实现则可能包含许多其他组件,并且可以将其对象分布在大型网络中的多个机器上。(图片)
图 2. J2EE 高层体系结构 注意,该体系结构允许您实现通过 Internet Inter-Orb Protocol(IIOP)直接与 EJB通信的客户机应用程序。这将向除基于浏览器的用户接口之外的交互类型敞开大门。
对于 J2EE 环境底层的一些主要概念和模型的理解是极为重要的。
请求—响应
基于 Web 的用户和应用程序服务器之间的交互遵循使用 HTTP 协议的原始 Web 模型。这意味着我们要受上面简要描述过的请求-响应模型的约束。
为了便于进行该通信,引入了新的 Java 类。用于处理该通信的主类就是 HttpServlet 类。该类包含了一组与HTTP 协议相匹配的方法,其中获得请求内容的方法是 doGet() 和 doPost()。
HTTP 协议要么通过在 URL 中放置参数来向 Web 服务器传递信息,要么独立于 URL 传递信息。第一种称作 GET 命令,而第二种称作 POST 命令。GET 命令的优点是,URL 包含了检索请求信息所需的所有信息。因此,可将其加入书签以便将来重新调用。而 POST 命令独立于 URL 发送附加信息,提供了更好的安全性。当需要发送比较大量的信息时,该方法也更为合适。其缺点就是不能被加入书签。
doGet() 和 doPost() 接收两个参数:HttpServletRequest 请求和HttpServletResponse响应。这些附加类为您提供了需要从请求中获得的所有信息。您将使用响应参数来编写应答。已提供的方法可以满足您完成该应答所需的所有功能。
浏览器和应用程序服务器之间的一切交互都是通过 HttpServlet类完成的。您得花些时间去学习上述类中所包含的字段和方法。
模型—视图—控制器
J2EE 建议使用 MVC 开发模型。该模型背后的思想是:尽可能地将与用户的交互、处理以及数据访问分离开。该模型已经活跃了很长一段时间。我记得它最早出现在20 世纪 80 年代的 Smalltalk 语言中。
图 3 说明了该模型与 J2EE 组件的使用。应用程序首先发出一个 JSP(视图)请求,而 JSP 稍后将返回一个在浏览器中显示的页面。接着,用户选择一个动作,向 servlet(控制器)发送信息。再由 servlet 来决定必须完成什么操作,它可能需要检索一个 Java bean(模型)或 EJB 来提供数据访问。然后,servlet 可以向 JSP 传递一个对 Java bean 的引用,以便访问要格式化和显示的数据。(图片)
图 3. MVC 模型 Enterprise Java Beans(EJB) EJB 体系结构提供了开发分布式应用程序的标准模型。一个 EJB包含两个接口:Home 和 Remote。home 接口用于创建或查找指定类型的对象。remote 接口是通过 home接口检索得到的,为您提供了对远程对象的公共方法的访问。
EJB 有三种类型:会话(session)、消息(message)和实体(entity)。会话 bean 提供对业务过程的访问。它们分为两种类型:有状态的和无状态的。有状态会话 bean 保存特定客户机调用之间的信息。无状态会话bean 可以被多个客户机共享,因为它不保存调用之间的任何特定信息。
消息 bean 是一种无状态的 bean,提供了用于操纵 JMS 异步消息的业务过程。实体 bean 代表业务数据及其相关的操纵逻辑。该业务数据必须保存在持久性存储器(例如,一个数据库)中。EJB是通过部署描述符进行分布的,部署描述符包括事务属性、安全性授权和持久性等信息。
面向对象方法(OOA)面向对象(Object Orientation)改进了软件的开发,它也是 J2EE环境中的关键部分。OO 的关键包括数据封装和继承的概念。
面向对象支持层次结构方法。我们可以看到对象继承层次结构和对象组合层次结构。图 4 通过部分医学数字图像和通信(Digital Imaging and Communications in Medicine,DICOM)标准,说明了这两种类型的层次结构。(图片)
图 4. DICOM 层次结构 一个 Data 对象(DICOMData)可以特殊化为以下四种对象中的一种:patient、study、series 或 image。而在图 4 所展示的另一层次结构中,则可以将 TAG 对象特殊化为 DataElement。这些类型的继承在OO 分析、设计和编程中极为普遍。仅仅查看 Java 类就可以看到一组精巧的对象继承层次结构。
图 4 还展示了聚集/组合示例,包括组合层次结构中的多个层次:我们看到,一个 Patient 可以包含多个study,而一个 study 可以包含多个 series,而一个 series 又可以包含多个image。我们还看到所有这些对象都可以包含多个TAG/DataElement,因为该组合是在对象继承层次结构的父类(DICOMData)中表示的。
聚集/组合模型使对象之间不太明显的区别变得有意义。阅读 OO文献时会发现,在多数商业应用程序中,具有频繁搜索并直接操纵的“第一级”对象,以及仅通过第一级对象来访问“第二级”对象 —— 如果您不检索第一级对象,就永远无法获得第二级对象。30 年前,这种典型的层次结构模型主导了数据库模型。我们将在后面的小节中进一步讨论它所带来的优点和缺点。
OO 中另一个有趣的主题就是对象持久性。实际上,OO 文献整个都在讨论对象,以及它作为内存中的对象与其他对象的交互。另一方面,持久性似乎是个很麻烦的问题。主要问题就是“持久保存对象”。数据库服务器除了在必要时恢复对象之外,不会添加值。这就是为何“阻抗失谐”问题对于许多OO 人员来说显得如此重要的部分原因。
J2EE 持久性模型
在谈论 J2EE 环境中的持久性时,我们很可能要涉及实体 bean。J2EE 环境为实体 bean 提供了两种持久性模式:bean 管理和容器管理。Bean 管理的持久性将所有的工作交给 EJB 开发人员,让他们确定如何存储和检索特定的对象类型。
容器管理的持久性从 EJB 的实现中去除了持久性的细节。部署描述符中包含的抽象模式定义了实体 bean 的持久性字段,以及它与其他 bean 之间的关系。这一工作是通过 EJB QL 语言完成的,该语言是 SQL 92 标准的一个子集。
在创建一个实体 bean 时,容器管理的持久性会将其信息保存在数据库中。每当一个方法调用修改该 bean 的内容时,这些修改就必须在数据库中加以反映。当然,这也要考虑目标实体 bean 的事务属性。
J2EE 试图让对象持久性尽可能地简单、透明和自动。在此过程中,它希望使持久性尽量独立于所有的数据库产品,而无需考虑使用何种类型的数据库或持久存储器。因此,数据库被降级为简单的持久性存储设备。
IBM 的对象—关系数据库
IBM 提供了两种最新型的对象-关系数据库:DB2? Universal Database(UDB)和 Informix Dynamic Server(IDS)。这两种都是关系型的数据库,因而包含了关系模型及其集合处理的长处。它们还包含了对象的概念,并且可以扩展数据库服务器的功能,更好地配合您的业务模型。而数据库服务器则是一个用于处理业务数据的可扩展性的架构。这些可扩展性特性与J2EE 环境中的概念一致,因为 J2EE 确实是一个可扩展的应用架构。
这些数据库产品在数据库市场中处于领导地位。要了解这些产品是如何崛起的,我们必须先看一看关系数据库为何成为了该行业的主导。在关系数据库出现之前,占主导地位的各种数据库都是按照层次结构来组织的。这应用了“分而治之”的概念。
从层次结构的顶层开始,您要选择一个指定节点来表示一个容器对象,如区域或部门,其中包含的是您数据库中所有数据的一个子集。该对象有可能包含成员。然后,您可以通过该节点的成员指针来选择另一个子集。该过程可以一直进行到您找到了需要操作的指定元素为止。除了可以添加、删除或修改元素之外,您还可以通过操纵指向元素或节点的指针,将元素移动到该层次结构中的另一位置上。
层次型数据库具有两大优点:
它们是系统开销最小的轻量级数据库,因为它们仅仅返回应用程序所请求的节点和成员指针。它们快速地将数据分成较小部分以获得所需的记录。另外,它们还具有一些可改善性能的特点:
针对单个问题的优化:本质上,层次数据库就通过一条指定的路径来优化数据访问。例如,一家大型银行可能按区域、部门和帐户来划分单元。那么,就可以极其高效地找到属于已知部门和区域的指定帐户,或者按区域和部门制作报表或进行分析。但是,要为某个指定客户找到所有帐户就比较困难了,因为该客户可能有多个过去创建的帐户。而且,该客户可能经历过多次职位和住所的改动,从而会拥有多个部门和多个区域中的帐户。
大多数公司都要通过他们的数据来解决多重问题。如果遵循层次模型,他们可以很好地解决某一个问题。而其他问题的解决则可能比较糟糕。为了取得相当好的性能,可能需要在多个层次中复制数据,从而可能极大地增加数据管理的复杂性。
对于物理记录的处理:层次数据库中所存储的记录是由应用程序来直接操纵的。应用程序必须知道每个字段的次序和类型,因为它必须计算该字段在记录中的偏移量。修改了记录结构就需要修改访问这些记录的应用程序。以编程方式实现对象的重定位:该指针操纵为复制和悬空指针(dangling pointer)等问题的发生制造了机会。
总的来说,层次模型在某些特定的应用程序中是一种极为可行的模型,IBM IMS 产品的长久生命力就证明了这一点。关系型数据库已经超越了持久性存储设备的角色,它们还解决了上述层次数据库的两个缺点。关系型数据库的主要特点有:
扁平的层次结构(Flattened hierarchy):关系数据库以表的形式来表示数据。所有的表都处于同一层次。这意味着所有的数据都可以直接进行访问。再回过头来看上文中的银行示例,我们可以直接访问所有帐户,并且找到属于指定客户的记录,而不必管其帐户属于哪个部门或区域。
逻辑记录:表中的列是通过列名而非记录偏移量来访问的。操作中只会使用指定的列。这样一来,应用程序可以独立于数据库中的列号以及各列次序。可以对表进行修改以添加新的列,且无需修改任何应用程序。这一概念随着视图(VIEW)的使用得到了深化,视图是一种由一个或多个表的子集所构建的虚表(virtual table)。您可以将视图的概念与对象接口等同起来。只要接口保持不变,使用该接口的应用程序就不需要进行修改。
集合操作:不是简单地检索特定记录并返回给应用程序,关系型数据库具有操作数据的功能。这些功能包括排序、分组、聚集以及一个操作不同数据类型的大型函数集。
非过程化的查询语言:关系型数据库包括一种称作结构化查询语言(Structured Query Language,SQL)的数据操作语言。这将允许用户或应用程序开发人员描述需要操作哪些数据,而不是描述如何获得该数据。然后,数据库系统必须确定如何实现该请求。优化器使用表大小、索引可用性以及数据分布等信息来确定响应该查询的最佳路径。
关系数据库还包括事务 ACID 属性(原子性、一致性、独立性、持久性)。关系数据库中陆续添加了约束、存储过程、备份/恢复、复制等功能。
IBM 在继续改进它的ORDBMS。总的方向包括性能、可用性、可伸缩性、可管理性、开发生产率、集成信息以及商业智能。通过数据联邦和自主计算(autonomic computing),在信息集成和随需应变计算方面取得了较大突破。IBM 的产品中已经包含了其中的某些功能,不久,还会出现更多功能来简化数据管理。
ORDBMS 的“OR”部分是什么呢?它是关系数据库的可扩展性部分。我们一旦在概念上完成这一飞跃,就会了解数据库的可扩展性是关系数据库的自然演变的一部分。
很早以前,关系数据库就包含了有限的可扩展功能。诸如约束、存储过程等功能都是可扩展性的一种形式。DB2 UDB 还包含了允许系统和数据库管理员设计部分处理的“用户出口”。存储过程难道还不足够吗?不。它们是为了在数据库返回数据之后应用过程化处理而设计的。其好处在于限制数据库与应用程序之间的数据传送。存储过程并未集成在数据库引擎中,因为它们被设计为以过程化的方式一次处理一条记录。我们还必须区别存储过程和存储过程语言。
DB2 支持用户用 SQL、Cobol、“C”和 Java 等语言编写存储过程。IBM-IDS 包含了一种存储过程语言,同时也支持用“C”和 Java 编写的存储过程。这些语言的使用已经扩展到了关系模型中。除了通过编写过程,在数据库服务器完成其处理之后操作数据,我们还可以将该处理包含在数据库系统的集合处理中。其结果就是将集合处理交给数据库引擎的简化函数。还可以将其执行更好地集成起来,从而更早地应用在处理中,以减少集合处理以及并行执行查询过程中的函数集成等后续步骤中的数据处理。这就支持多线程地执行这些函数,而不必通过复杂的代码来显式地利用它。
DB2 UDB 和 IDS 允许您编写多个具有相同名称的函数,但必须带有不同数目或不同类型的参数。这类似于适用于integer、decimal、float、double或其混合类型的加法运算符(“+”),也类似于面向对象中的多态性。除了函数,这两种数据库还允许您创建新的类型,以及通过使用表层次结构创建对象层次结构。所有这些功能共同允许您通过调整数据库来配合业务环境,而不是牺牲您的设计来适应数据库。深入探讨您通过充分使用 IBM 数据库的功能所获得的所有商业优势我们将在下一连载中继续为您讲述。
IBM ORDBMS 数据库提供了大量功能,可以简化软件开发,减少硬件需求,以及加快进入市场的步伐。您必须在设计中善加利用,以便从中受益。IBM ORDBMS 不仅仅是一个持久性存储器,更是一条提高您生产率和效率的路径。
非同小可的对象危机
理论上,以及在许多实际案例中,对于持久对象的操作需要实例化该对象,对其进行操作,以及与持久性存储器保持同步。这就导致了容器管理的持久性以及独立于数据库的思想:“使用容器管理的持久性的好处是,实体bean 可以在逻辑上独立于存储该实体的数据源。…”
该规范还提到数据源可以是关系型的,也可以是非关系型的,如 IMS。当然还可以是面向对象的数据库。任何修改容器管理 bean 的交互都需要与该数据源保持同步,以确保实体 bean 的一致视图。对 Enterprise JavaBean(EJB)使用chatty 接口会显著地增加数据库的交互量,从而可能导致性能问题。
“每次实例化一个对象”的中心思想有可能会引起性能和容量问题。该问题不是仅限于容器管理的实体 bean 的使用中,而是普遍存在于面向对象方法中。让我们用一个简单的示例来加以说明。假设有一家专门从事贷款发放的大型银行。它遍及多个区域,每个区域包括几十甚至上百家支行。(图片)
图 5. 银行贷款机构 图 5 说明该银行对应多个区域,每个区域都有多个支行,而每个支行又进行多项贷款。它还显示我们可以获得不同种类的贷款。
如果该银行需要获得一份清单,列出那些在贷款中承担了过高风险的支行,它就会按区域来收集该清单。每个区域将搜索其支行,找到那些风险过高的。而支行本身必须查询每一项贷款的风险和贷款额,用以计算该支行的平均风险。
以下是一种适宜的对象方式:每个对象封装自身的信息和处理。支行要获得贷款风险的惟一方法就是向该贷款查询这一信息。该方式比较合理,因为它消除了对象类型之间的紧密耦合。对象间的通信取决于定义良好的接口。只要该接口保持不变,就可以修改其实现,且不影响整个系统。
本例中,如果我们考虑:该银行遍及 10 个区域,每个区域有 100 家支行,而每家支行又有 10,000 份贷款,我们总共创建了1000 万多个对象来响应该查询。要求每份贷款返回两个值:风险级别和贷款额。在区域和支行之间的通信中,这会在两个对象之间传递超过2000 万条消息。最后从内存中删除这些对象需要进行额外处理。
所使用的数目(10/100/10,000)丝毫不过分,我们绝对可以想像将生成更大数量级对象的系统。这很容易成为一个性能以及容量(例如内存)问题。若通过给应用程序服务器增加节点来解决该问题,非但解决不了,反而会增加复杂性。
通过利用 DB2 和 IDS等对象-关系数据库的长处,即使无法消除该问题,也可以有所缓解。请记住,大多数面向对象人员仅仅将数据库看作持久性存储器。他们习惯于将所有处理放在对象的代码中完成,事后才添加数据库。
如果考虑面向对象是何时成为主流的,我们就可以设想,现在绝大多数 30出头或更年轻的程序员已经被训练成以这种方式来思考了。由于数据库仅仅用于“持久保存”对象以及检索它,所以系统开销最小的数据库在该模型中就占有优势。这会有利于层次、网络和对象数据库。而对象-关系型的数据库可以完成比持久保存对象多得多的功能,却成为永远不被启动的大引擎。这就好比是在一场用赛车对抗自行车的比赛中,却不允许您发动引擎一样。
让我们来解释一下:面向对象方法是优秀的。问题在于许多架构师和开发人员视野过于狭隘,不知道可以选择将处理置于何处。这一选择结果会影响设计,并且可以带来重大性能影响。利用ORDBMS的长处可以简化设计,以及极大地提高结果系统的性能。这相当于加快进入市场的步伐,以及降低开发和维护的成本。更快的结果还可以带来重要的商业优势。
数据库独立性
我在前面已提到 J2EE通过促进数据库独立性来提高应用程序的可移植性。并且还进一步提到可以是任何类型的数据库:关系(对象-关系)或非关系的。这是整个J2EE 体系结构的一个美好目标,但在构建商业应用程序中却是一种十分危险的方法。
商业应用程序的目标应该是提供对抗竞争者的商业优势,而非可移植性。可移植性是一个次要目标。如果需要进行移植,可以将不可移植的部分隔离起来,以便限制所需的工作。我们需要以尽可能最低的成本获得尽可能快的响应。如果您针对移植性进行设计,就要设计最底层的功能,从而放弃所有优势。这就像雇主拒绝雇用一个高度称职的人,因为怕他某一天离开,而下一任雇员的能力可能远远不及。按照该逻辑,我们应该雇用最无能的人。否则的话,我们应该确保限制雇员对公司的贡献,这样,如果我们哪天必须用一个逊色一些的人来接替他时,就不会感到失望。期望越多,您将得到越多。而期望越少,您得到的也会随着时间越来越少。
这同样也适用于企业应用程序。您应该权衡您的数据库系统的所有功能,并且对于能够带来商业优势的功能善加利用。您可以在设计中隔离数据库的交互,以便当您偶尔必须迁移到另一数据库时,就只需要完成有限的移植工作。由于数据库的竞争十分激烈,所以,很可能您现在计划使用的独特的新功能将来会出现在某个竞争对手的数据库中。或者,在数据库供应商为争取您的业务,协商将如何补偿其缺点时,这可能成为其中的一个缺点。因此,设计要谨慎,但也要赢得成功。
J2EE 复杂性
J2EE 具有许多好处,有助于商业应用程序的开发。它们包括平台独立性、组件架构、多层应用程序模型、统一的安全模式以及一个丰富的标准集合,这些标准涉及事务控制、数据库访问和消息传递等领域。实际上,J2EE在一个架构下集成了过去 50 年左右软件中所取得的进展。这也付出了一定的代价:复杂性。
尽管 WebSphere Studio Application Developer 等工具明显地简化了开发,而且WebSphere Studio Application Developer 管理控制台提供了控制,在启动大型项目之前,我们还是必须确保已经具备了适当的专业知识。最有效的方法就是培训与聘请专家顾问相结合。
同样重要的是,要具有一支包括了各个领域专家的综合队伍。在项目启动之前,要将这支队伍聚集在一起。例如,数据库管理员(DBA)和SQL 专家应该从一开始就一起讨论将如何使用不同的对象。让我们用一个示例来加以说明。
从 DICOM 对象之间的关系。我们看到,每一种类型的 DICOMData 都可以包含许多其他的 DICOMData 对象。我们可以通过标准的关系方法或使用新的数据类型,在数据库中表示该层次结构关系。一个数据元素是由一个标签(tag)和一个值表示。存在不同类型的值,如字符串、日期(date)、小数(decimal)等一个值还可以具有重复字段。一个DICOMData 对象有可能包含几百种不同的数据元素。但实际上,数目要少得多。
将一个 DICOMData 对象表示为一大行几乎为空的数据元素既不实际,也不可能。那么,我们可以将 DICOMData 对象表示为 DICOM 对象本身与数据元素之间的关系。第一种方法是为每一种类型的数据元素准备一个数据元素表。这会产生 23个表,而且检索一个 DICOMData 对象要连接 24 个表。其他的方法可能允许我们将 23个表缩减为一个。即便如此,通过关系型数据库来存储和检索 DICOMData 对象也要付出昂贵的代价。
如果数据库专家恰好从一开始就参与进来了,那么就可以讨论这些问题,并可以揭示那些使实现更加容易的新信息。结果显示每种类型的 DICOMData 对象中只有少数元素将用于搜索。这就允许我们表示 DICOMData 对象中的少量值,并且将所有其他的数据元素组合成一个大型对象列。在存储和检索 DICOMData 对象时,这带来了显著的性能提高。
在前一小节中,我们讨论了通过向每个支行查询贷款上所承担的平均风险度导致的对象爆炸(见图 5)。DB2 UDB 和 IDS 的对象-关系功能允许我们扩展数据库功能,以便在数据库中包含风险计算函数。那么,我们可以使用存储过程或用户定义的聚集来计算每个支行的平均风险。我们甚至可以添加条件,其中定义了不能接受的风险级别,并且规定只检索处于过高风险中的支行清单。结果带来了显著的性能增长,因为我们不必实例化一百万个对象,并且避免了对象之间的多数消息传递。在对象人员看到了许多对象创建和通信的地方,数据库人员可以找到直接提供对策的办法。在恰当的地方进行处理将提供更简单、更高效的解决方案。
J2EE 的其他方面也增加了它的复杂性。这包括牵涉共享 EJB 的多线程环境。您如何共享 EJB呢?在一次更新中又如何协调多个 EJB呢?您的代码是可重入的,还是设置必须锁定的临界区呢?这将导致许多问题,如数据完整性、锁定策略以及数据库人员所熟知的一些问题。
那么,存在 EJB 访问的性能问题,您需要以怎样的粒度进行才能获得高性能。您还必须确定如何监控应用程序以找到性能瓶颈。
J2EE环境在开发和运行企业应用程序中表现极佳。但由于其复杂性,不宜滥用。如果希望项目取得成功,您就要在初期组织好一支合适的专家队伍来共同完成。
寻求解决方案本文前面所提出的问题没有理想的解决方案。要从正确的培训抓起,这样您才能充分使用解决方案的所有组件。其中可能包括在 Web 浏览器中使用 applet、中间件当中的合理设计、以及利用数据库系统的长处。(图片)
图 6. 对象集合 上面的示例说明应该仔细检查一个对象里对象集合的使用。我们必须确定是否真的需要一个集合,或者是否可以用一个或多个通过数据库操作该对象集合的方法来代替该集合。在有些情况下,保留该集合将是比较恰当的;而在其他情况下,使用方法将提供更大的好处,例如性能的提高和复杂性的减小。
图 6说明了中间件中对象的创建以及对象之间的通信。如果我们可以直接从数据库获得想要的信息,那么我们就可以从多个地方获得性能提高。首先,我们可以通过避免在数据库服务器和应用程序服务器中的应用程序之间传送大量数据来获得更好的性能。该传送可以在网络连接上的两台机器之间,通过网络所连接的同一机器上,或使用共享存储器所连接的同一机器上进行。当在两台机器之间进行通信时,数据传送要受到以下因素的影响:它必须通过的多个硬件组件(存储器、系统总线、网络控制器等)、网络延迟、在网络的不同层上向机器间传输的每个数据包添加附加信息的协议栈。
如果数据是在同一机器上传送的,它可能仍然要通过网络协议,并且包括操作数据包所需的所有处理。通过共享存储器连接来完成的开销最低。即便如此,它仍然包括数据库协议开销,以及从服务器空间到应用程序空间的内存复制。与处理器的速度相比,这种内存到内存的复制相对比较慢。
当应用程序向数据库请求数据时,会通过数据库连接串行化其结果。数据库服务器可以在内部并行地处理信息,以提供大得多的吞吐量。请记住,诸如DB2 UDB 和 IDS等数据库都是优化了数据集合操作的专用引擎。其算法已经由具有多年大型商业应用程序支持经验的专家进行了优化。所以,利用这些专家经验,而不是设法重新创建一个是有道理的。
应用程序服务器一旦收到了所有数据,它就需要实例化大量对象,然后逐个与之进行通信,获取进行所需计算必要的少量信息。对于这些对象的调用数目,以及为取得期望结果必须执行的代码行数,让我们了解要取得期望结果需要进行多么重大的处理。通过将数据库作为数据处理引擎,我们可以避免其中大部分开销。同时,还减少了解决方案的复杂性。
在将所有对象实例化之后,还需要创建从每个对象收集信息的适当处理,按照合适的分组将该信息分类,以及为最后的结果执行聚集操作。所有这些工作都可以通过使用数据库来消除。对象人员经常声称对象方法比过程化编程优越。当一个对象必须操作一个对象集合时,它有效地使用过程化代码来完成该操作。这就是将集合处理交给数据库完成的另一个原因。
操作对象的类型和操作类型可以影响是否使用对象集合的决策。我们以前曾看到,一些真正的第二级对象只有在检索更高一级的对象时,才能被检索。这可以说明应该尽可能地将它们留给数据库来处理。
某些处理需要从对象获取信息,如果该信息可以直接从数据库服务器获得,我们就可以避免实例化它们。不仅上面银行示例中的聚集计算是如此,报表编制也属于这种情况。在后面一种情况下,将对象实例化以报告某些信息之后,就再也不需要该对象了。
即使在对象更新它们状态的情况下,也应该谨慎地考虑对象实例化。它仍然涉及对象创建、对象之间的消息和新状态的持久性。从数据库直接获取数据可能更合理。与将数据库中的对象视为被动的(passive)相反,我们至少应该将其当作半主动(semi-active)的。这种观点的改变可能会引入不同的应用程序设计方法,结果将会是充分利用数据库来实现解决方案。
结束语
一个强大的 J2EE 应用程序会充分利用它所有组件的长处。这包括浏览器或用户接口部分、应用程序服务器和数据库引擎。通过将处理置于最为合理的地方,可以减少应用程序的复杂性,并且极大地提高性能。
数据库的选型应该是会为您带来商业优势的战略性决策。一旦做出决策,您就必须充分利用该数据库服务器来实现这一优势。数据库系统至关重要,通过使用数据库服务器的所有功能,您可以消除一些数据移动,减少对象的创建和对象间的通信,简化您的实现,以及更快地进入市场。充分使用了所有组件功能的均衡实现就不会需要那么多的硬件和代码。这就减少了构建和维护该应用程序的成本,可以将所节省的资金运用到企业的其他地方,从而提高您的商业竞争力。
我们需要可以将数据库功能集成到所有 OO 开发阶段(包括分析、设计和实现)的更好的工具。这包括分析、设计和实现。同时,我们必须通过在项目的前期召集数据库专家,来确保这些任务的正确完成。
3/20/2005
|