如果您认为 J2EE 编程模型和 API 迫使开发人员在特定于技术的配置、编程和调试上浪费了太多的时间,那么欢迎您阅读本文。很多 Java™ 开发人员都怀疑如何能以统一的方式访问异构的数据,并对各种提出要解决这一问题的编程框架感到失望。在本文中,Java 开发人员 Bertrand Portier 和 Frank Budinsky 将介绍使用服务对象数据(SDO)的下一代数据编程。
简单地说,SDO是一种数据应用程序开发框架,它包括一个体系结构和相应的 API。SDO 能够实现以下操作:
在这篇介绍 SDO 框架的文章中,我们将尝试着解释 SDO 背后的动机,以及 SDO 与其他规范的区别,然后描述 SDO 的组成,最后,通过一个示例 SDO 应用程序说明 SDO 的用法。
为什么要使用 SDO?
对于服务数据对象(SDO),大多数开发人员要问的第一个问题就是为什么要使用 SDO。难道 J2EE 本身还不够庞大、不够复杂(而且难以掌握)吗?Java 环境中不是已经有其他支持 XML 的框架了吗?所幸的是,该问题的答案能够让我们多数人感到满意:SDO 是作为简化 J2EE 数据编程模型的方法出现的,它能够让 J2EE 开发人员把更多的时间用于应用程序的业务逻辑。
服务数据对象框架为数据应用程序开发提供了统一的框架。通过 SDO,您不需要熟悉特定于技术的 API,就能访问和利用数据。您只需要知道一种 API,即 SDO API,它允许您处理来自多种数据源的数据,其中包括关系数据库、实体 EJB 组件、XML 页面、Web 服务、Java Connector Architecture、JavaServer Pages 页面等。
注意,我们使用了 框架这一词。这是比照 Eclipse 框架的说法。由于设计的基础坚固而且可以扩展,所以 Eclipse 能够集成各种工具。与此类似,对于采用 SDO 的应用程序,SDO 也是一种框架,这类应用程序在 SDO 模型上是一致的。
与其他某些数据集成模型不同的是,SDO 没有停留在数据抽象上。SDO 框架还结合了很多 J2EE 模式和最佳实践,从而使应用程序很容易结合经过验证的体系结构和设计。比方说,今天的多数 Web 应用程序百分之百的时间没有(或不能)连接到后端系统,因此 SDO 支持不连接的编程模型。同样的,今天的应用程序往往非常复杂,包含很多层次。如何存储数据、如何发送数据、如何在 GUI 框架中将它们提供给终端用户?SDO 编程模型提供的应用模式能够清晰地划分不同的问题。
XML 在分布式应用程序中日渐普及。比如,XML Schema (XSD) 被用来定义应用程序数据格式中的业务规则。XML 本身也可以用于改进交互性:Web 服务使用基于 XML 的 SOAP 作为消息格式。XML 是推动 SDO 的一个重要原因,SDO 框架支持并集成了 XML。
|
各种技术的比较
前面已经提到,SDO 并不是提出解决分布式应用程序中数据集成问题的惟一技术。下面将分别讨论 SDO 和类似的编程框架 JDO、JAXB 和 EMF 的优劣。
SDO 和 WDO
Web 数据对象(或 WDO)是随着 IBM WebSphere® Application Server 5.1 和 IBM WebSphere Studio Application Developer 5.1.2 发布的 SDO 早期版本的名称。如果使用过 WebSphere Studio 5.1.2,那么您对 SDO 可能已经有所了解,虽然您可能习惯看到将它标记为 WDO,比如在数据库的名字中。忘掉 WDO 吧,它现在的名字是 SDO!
SDO 和 JDO
JDO 代表 Java Data Object(Java 数据对象)。JDO 已经通过 Java 社区进程(JCP)标准化了 1.0 版,2003 年 5 月推出了维护版 1.0.1,现在已经为 2.0 版成立了 JCP 专家组。JDO 针对 Java 环境中的数据编程提供了一种通用 API,用于访问存储在不同数据源中的数据,如数据库、文件系统或者事务处理系统。JDO 保持了 Java 对象(图)之间的关系,同时允许对数据的并发访问。
JDO 希望简化和统一 Java 数据编程,以便开发人员能够专注于业务逻辑,而不是底层的技术,从这一点上说,其目标和 SDO 是相同的。但主要的区别在于,JDO 仅考虑持久性问题(J2EE 数据层或者企业信息系统(EIS)层),而 SDO 更具一般性,关注的是不同 J2EE 层次间数据流的表示,比如表示层和业务层。
有趣的是,SDO 可以结合 JDO 一起使用,JDO 作为数据源,SDO 来访问它,这就是 Data Transfer Object(数据传输对象,DTO)设计模式的具体运用。同样,SDO 也可以结合实体 EJB 组件和 Java Connector Architecture(Java 连接器体系结构,JCA)使用,目的是提供统一的数据访问。
SDO 和 EMF
EMF 代表 Eclipse Modeling Framework(Eclipse 建模框架)。EMF 根据使用 Java 接口、XML Schema 或者 UML 类图定义的数据模型生成统一的元模型(称为 Ecore),可以将元模型与框架结合在一起使用,创建高质量的模型实现。EMF 提供了持久性、一个有效的反射类属对象操纵 API 和一个变更通知框架。EMF 还包括用来构建 EMF 模型编辑器的一般的重用类。
EMF 和 SDO 都可以处理数据表示。事实上,IBM 的 SDO 参考实现就是一种 SDO 的 EMF 实现,后面我们还要用到该实现。还可以根据 UML 模型定义或者 SDO 本身,用 EMF 代码生成来创建 SDO 实现的某些部分。SDO 实现基本上是 EMF 中的一小层(方面),它是作为 EMF 项目的一部分打包和提供的。关于 EMF 的更多信息,请参阅 参考资料。
SDO 和 JAXB
JAXB 代表 Java API for XML Data Binding(XML 数据绑定 Java API)。JAXB 1.0 在 2003 年 1 月由 JCP 发布。JCP 专家组已经制定了 2.0 版的初步草案。JAXB 是关于 XML 数据绑定的,也就是说,将 XML 数据表示成内存中的 Java 对象。作为 Java 语言的 XML 绑定框架,JAXB 可以节约解析和创建 XML 文档的时间。(事实上,通过它,您完全不需要和 XML 打交道。)JAXB 为您执行编组/序列化(Java 到 XML)和解组/并行化(XML 到 Java)。
SDO 定义了自己的 Java 绑定框架,但是走得还要远一些。JAXB 仅关注 Java 到 XML 的绑定,而绑定到 SDO 的不仅仅是 XML 数据。如前所述,SDO 提供了对不同类型数据的统一访问,XML 仅是其中的一种数据类型。SDO 还提供了静态和动态 API *,而 JAXB 仅提供了静态绑定。
* 注意,本文中示例应用程序仅使用了动态 SDO,虽然 EMF 代码生成器也为数据对象的静态代码生成提供了完整的支持。
SDO 和 ADO .NET
ADO 代表 ActiveX Data Objects(ActiveX 数据对象),但是在 .NET 上下文中,其含义已经发生了变化。ADO .NET 提供了 .NET 框架下不同层次间的统一数据访问机制。
ADO .NET 和 SDO 具有相同的动机,都是用来支持 XML 和分布在多个层上的应用程序。除了技术性的差异外,这两项技术主要的不同在于:ADO .NET 用于 Microsoft .NET 平台,是一种私有的技术;而 SDO 用于 Java (J2EE) 平台,通过 Java 社区进程实现标准化。
|
SDO 的组成部分
这一节将介绍 SDO 的体系结构。我们将介绍组成框架的不同组成部分和它们之间的协作。首先要讨论的三个组成部分之一是 SDO 的“概念性”特征,API 中没有对应的接口。
SDO 客户机
SDO 客户机使用 SDO 框架处理数据。SDO 客户机使用的不是特定于技术的 API 和框架,而是 SDO 编程模型和 API。SDO 客户机处理 SDO 数据图(参见 图 1),不需要了解所处理的数据是如何持久保存或者序列化的。
Data 中介服务
数据中介服务(DMS)负责从数据源创建数据图、依据数据图的变化更新数据源。数据中介框架不属于 SDO 1.0 规范,换句话说,SDO 1.0 没有涉及具体的 DMS。常见的 DMS 有 JDBC DMS、实体 EJB DMS 和 XML DMS 等。
数据源
数据源不限于后端数据源(如持久存储数据库)。数据源以自己的格式保存数据。只有 DMS 访问数据源,SDO 应用程序不访问数据源。SDO 应用程序可能只使用数据图中的数据对象。
下面介绍的每个组件对应于 SDO 编程模型的一个 Java 接口。SDO 参考实现(请参阅 参考资料)提供了这些接口基于 EMF 的实现。
数据对象
数据对象是 SDO 的基本组件。事实上,它们是规范名称中的 服务数据对象。数据对象是结构化数据的 SDO 表示。数据对象是通用的,它们提供了 DMS 创建的结构化数据的公共视图。比方说,虽然 JDBC DMS 需要知道持久性技术(比如关系数据库),以及如何配置和访问数据,SDO 客户机不需要了解这些细节。数据对象在属性中保存它们的“数据”(稍后还要讨论属性)。数据对象提供了易于使用的创建和删除方法(带有不同签名的 createDataObject()
和 delete()
),获得自身类型(实例类、名称、属性和名称空间)的反射方法。数据对象都链接在一起,包含在数据图中。
数据图
数据图提供了数据对象树的容器。数据图由 DMS 生成,供 SDO 客户机使用。修改后,数据图被回传给 DMS 更新数据源。SDO 客户机可以遍历数据图,读取和修改数据图中的数据对象。SDO 是一种 无连接的体系机构,因为 SDO 客户机与 DMS 和数据源没有连接,所以 SDO 客户机看到的只有数据图。此外,数据图可以包含表示不同数据源中数据的对象。数据图包含一个根数据对象、与根关联的所有数据对象和变更摘要(change summary,参见本文后面的叙述)。当在应用程序组件(比如服务调用期间的 Web 服务请求者和提供者)之间进行传输、组件到 DMS 的传输(或者保存到磁盘)的时候,数据图被序列化为 XML。SDO 规范提供了序列化的 XML Schema。图 1 显示了一个 SDO 数据图。
变更摘要
变更摘要包含在数据图中,表示对 DMS 返回的数据图的修改。变更摘要最初是空的(数据图刚返回客户机的时候),随着数据图的变化逐渐填充。在后台更新时,DMS 使用变更摘要将修改应用于数据源。变更摘要提供了数据图中被修改的属性(包括原来的值)、新增和删除的数据对象的列表,从而使 DMS 以递增方式高效地更新数据源。只有当变更摘要日志功能被激活时,才会将信息添加到数据图的变更摘要中。变更摘要提供了让 DMS 打开和关闭日志功能的方法,后面的例子中还将详细对其进行介绍。
属性、类型和序列
数据对象用一系列属性保存其内容。每个属性都有一个类型,该类型既可以是基本类型(如 int
)这样的属性类型,也可以是通用数据类型(如 Date
),如果引用的话,还可以是其他数据对象类型。每个数据对象都为属性提供了访问和设置方法(getter 和 setter)。这些访问器方法有不同的重载版本,可以通过传递属性名( String
)、编号( int
)或者属性元对象本身来访问属性。String 访问器还允许使用类 XPath 的语法访问属性。比如,可以对公司数据对象调用 get("department[number=123]")
来访问编号为 123的第一个部门。序列更加高级,可以保持不同种类的属性-值对列表的顺序。
|
SDO 和示例安装
我们已经讲了很多的概念和理论,现在要进行一些实际操作了。好消息是您马上就能使用 SDO,而且是免费的。这一节将介绍一个 SDO 示例应用程序,它将在 SDO 的 IBM 参考实现上运行,这个参考实现是 Eclipse Modeling Framework (EMF) 的一部分。我们首先将说明如何安装 EMF 2.0.1(包括 SDO),然后将介绍如何设置本文提供的示例应用程序。
安装 EMF 2.0.1
如果已经安装了 EMF 2.0.1,或者知道怎么安装,那么请跳过这一节,直接阅读下一节。
IBM SDO 1.0 实现和 EMF 2.0.1 一起打包。要使用 SDO,则需要安装 EMF 2.0.1 *。您可以按照 EMF 站点上的说明使用 Eclpise 更新管理器,也可以按照下面的步骤操作。
*EMF 2.0.0 中也包含 SDO 1.0 的实现。
在 EMF 主页中的“快速导航”部分可以找到下载链接页面,选择下载“v2.x: EMF 和 SDO”。安装 EMF 之前一定要阅读安装要求。安装 EMF 2.0.1 之前至少要安装了 Eclipse 3.0.1 和 Java Development Kit (JDK) 1.4。一定要选择 EMF 2.0.1 发行版本。打包类型建议选择“All”:emf-sdo-xsd-SDK-2.0.1.zip,这样,就可以在一个文件中找到源代码、运行文件和文档。如果愿意,还可以下载 SDO 的最小安装包“EMF & SDO RT”:emf-sdo-runtime-2.0.1.zip。
在解压 Eclipse 的目录中解压 zip 文件(压缩包中的文件组织结构为 eclipse/plugins/...)。启动 Eclipse,然后选择 Help>About the Eclipse Platform,检查 EMF 是否安装成功。单击 Plug-in Details按钮,确保 org.eclipse.emf.* 插件在 2.0.1 层次上。有 6 个与 SDO 有关的插件:
运行时只需要两个插件: org.eclipse.emf.commonj.sdo
和 org.eclipse.emf.ecore.sdo
,如果选择仅安装运行时插件,那么您只能看到这两个插件。这样就完成了 EMF 的安装。
安装示例 SDO 应用程序
下一步是在工作区中添加本文所用的 SDO 示例应用程序,步骤如下:
然后,单击本文顶端或底端的 Code图标(或者参阅 下载部分)下载 j-sdoSample.zip,并将其解压缩到 SDOSample 目录中(在 Eclipse 窗口中选择 Import... >Zip file)。一定要保留文件夹结构并覆盖原来的文件,这样,就可以用 j-sdoSample.zip 中的文件代替 SDOSample 项目。
注意:SDOSample 是作为 Eclipse 插件项目打包的,因此不需要自己设置库依赖关系。但是,在本例中,只包含 Java 代码,如果在 CLASSPATH 中包含 EMF 和 SDO 库(JAR 文件),也可将 SDOSample 作为独立的应用程序运行。
环境设置应该与下面截屏图中的类似。
现在开始使用这个示例 SDO 应用程序。
|
一个简单的 SDO 应用程序
本文后面将使用的示例应用程序从功能上说很有限,但它可以帮助您更好地理解 SDO。该应用程序包括两部分,分别放在两个包中:dms 和 client。
SDO 1.0 没有规定标准 DMS API。因此我们为这个例子设计了自己的 DMS 接口,它包含两个方法,如清单 1 所示。
|
客户机将实例化 DMS,并调用 get()
方法访问特定的雇员:Big Boss、Wayne Blanchard 和 Terence Shorter。它以用户友好的方式在控制台中打印这些雇员的信息,然后更新 Terence Shorter 及其雇员的部门信息。最后调用 DMS 的 update()
方法,传递更新的 Terence Shorter 数据图。
注意,为了便于示范,我们没有实现数据源组件,但在 DMS 中,有一些如何根据查询构建数据图的“硬编码”知识。图 3 显示了 DMS 正在使用的雇员层次结构。
图中可以看出,DMS 背后的虚拟公司有 4 个雇员,层次结构如下:
运行示例应用程序
右击 SDOClient.java,然后选择 Run>Java application运行上述示例应用程序,结果如下:
|
现在我们来分析应用程序的各个组成部分是如何工作的。
|
客户机
SDO 客户机例示了 DMS,并从中获得不同雇员的数据图。得到数据图后,通过根对象(使用 SDO 动态 API)遍历和访问数据对象,如下所示:
|
客户机调用动态 SDO 访问器 API,从数据对象中获得信息,并将其显示到控制台上:
|
我们已经看到客户机是如何获取信息(读)的,但是如何写入信息呢?更准确地说,客户机如何修改对象?SDO 客户机一般使用 DataObject
写访问器方法更新数据对象。比如,下面的代码修改从雇员 Terence Shorter 获得的数据图:
|
注意,客户机没有调用日志方法。DMS 负责对数据图的变更摘要调用 beginLogging()
和 endLogging()
记录日志。
|
数据图
可以将数据图的数据格式(模型)看作 DMS 与客户机之间的抽象。客户机期望从 DMS 得到数据图,DMS 也知道如何创建数据读(包括从后端数据源中读取和更新数据)。如果熟悉 XML 或者 Web 服务,那么可以将数据图模型看作定义数据对象 XML Schema(XSD)。数据图本身就类似 XML 实例文档。事实上,XML Schema 也是定义 SDO 模型的一种方法。
注意,数据图及其模型总是能够序列化为 XML。在 SDOClient.java 中将 debug
变量设为 true
,就可以看到运行时在控制台中显示的数据图的序列化版本,与清单 3 类似。
|
在本例中,数据图由 Employee
数据对象(以及变更摘要)组成。 Employee
包含姓名、编号、部门、职务、上司(管理该雇员的另一位雇员)和下属(该雇员管理的其他雇员)等属性。该例中,雇员已经位于硬编码的数据源中时,DMS 返回的数据图的格式总是雇员的上司(如果有的话)、雇员及其直接下属(如果有的话)。
|
DMS 构建数据图
SDO 1.0 没有规定 DMS API,API 应该包含数据图模型本身的设计和创建。因为建立对数据源的访问要考虑很多种情况,设计数据图本身可以作为另一篇文章的主题。
对这个例子,我们将采用 DMS 通过动态 EMF API 定义的雇员模型。示例数据图没有 XSD 这样的模型文档。实际上,因为对象是动态生成的,所以这意味着没有生成 Employee
Java 类。如果使用静态方法,就会生成这样的类。
DMS 使用不同的数据访问 API(JDBC、SQL 等)从不同数据源获取的信息。但是,一旦从后端(该例中是硬编码的知识)检索到信息,DMS 就转而使用 EMF API(eSet、eGet)而非 SDO API 来创建数据对象的数据图。这种方法能得到较好的性能,但缺点是不能跨 SDO 实现移植。
对于主要关注性能,同样的 DMS 设计也可使用 SDO API 实现。在这种情况下,DMS 类( employeeClass
、 employeeNameFeature
等)中缓冲的元对象,将代替 EMF 类型 EClass
、 EAttribute
和 EReference
等),成为 commonj.sdo.Type
和 commonj.sdo.Property
的类型。此外,如果性能不重要,那么可以方便地使用基于 String 的 SDO API(如 setBoolean(String path, boolean value)
),因此不需要缓冲元对象。不幸的是,这种解决方案虽然更方便,但运行起来也更慢。
下面的代码片段说明了 SimpleEmployeeDataMediatorImpl.java 中 Employee 模型的定义。但这些代码并没有构建 SDO 对象,构建的仅仅是 SDO 对象的模型:
|
注意,我们使用值 true
对 employees EReference
调用 setContainment
,因此每个雇员都“包含”他或她的下属。否则,嵌套的雇员就不会(即被包含)在数据图中,变更摘要也只能包含根对象的修改,而不能包含其他雇员的修改。
建模 SDO
您可能想“很有趣,但是这样只能得到 EMF 对象而不是 SDO 对象,还有什么窍门吗?”当然有,而且很简单。 Employee EClass
属于 employeePackage EPackage
,并且包含下面的调用:
|
运行时,这个工厂将创建 DynamicEDataObjectImpl
类型的对象,该对象实现了 DataObject
接口(即它是一个 SDO 数据对象),而不是默认的 DynamicEObjectImpl
接口,并且可以智能地创建普通的 EMF 对象。SDO 和 EMF 对象之间的关系就很清楚了:SDO 对象就是同时实现了 SDO DataObject
接口的 EMF 对象。事实上,这些附加方法的实现是委派给核心 EMF 方法实现的。
创建 SDO 实例
现在,已经拥有了我们的数据对象,就可以建立 Employee
的实例并设置不同的属性了。如前所述,我们将使用 EMF API 来最大程度地提高性能。
|
可以使用“employees”引用把雇员“链接”在一起,比如:
|
一旦创建了数据对象,还需要将其连接到数据图中。我们首先要调用数据图的 setRootObject()
方法,传递希望用作根的数据对象,这里使用 Employee The Boss
。
|
继续讨论数据图之前,要先开始记录日志。如果希望使用 SDO,那么在改动数据图之前,要先调用其变更摘要的 beginLogging()
方法。基本的原理就是清除以前的变化,开始监听新的修改。
|
DMS 的另一项任务(按照 EmployeeDataMediator
接口的定义)是根据 SDO 客户机提供的数据图更新后端数据源。
|
DMS:更新数据源
DMS 应该使用 SDO 的强大特性更新后端数据源,具体地说,就是要使用其变更摘要。使用数据图的变更摘要有许多不同的方法。该例中,我们将考察变更摘要树中引用的所有数据对象,并从中获得新的数据对象。
|
该例中根本没有后端更新。但在实际应用时,后端更新应该在这个方法中完成。
DMS 首先要对数据图的更新摘要调用 endLogging()
,从客户机取回数据图,以便进行后端更新。这样将关闭变更记录,从而提供自 beginLogging()
调用以来(通常在创建之后调用)对数据图所做修改的摘要。这种格式支持 DMS 高效、递增地更新后端数据源。变更摘要中的修改分为三种类型:
注意,我们使用标准的 SDO API 检查数据图的变化,虽然也可以使用 EMF ChangeDescription API(而不是 SDO 的 ChangeSummary)。该例中仅仅更新简单的属性值,对性能的影响不是很明显。但在其他情况下,比方说要更新大量的属性,使用 EMF API 可以显著地提高性能。比如,假设要从数百个雇员的列表中删除一个雇员。在这种情况下,ChangeSummary 只能提供对原有数百个雇员列表中的原有值的访问。 而 EMF 的 ChangeDescription 接口还可以提供更具体的信息,如“删除某个索引位置的雇员”,这就有用得多。
还要注意的是,该变更摘要中只修改了一个对象,没有删除或者添加对象。如果使用 SDO 实现从数据图中删除对象,还要注意类型为 objectsToAttach 的元素。该名称实际上是对象删除的 EMF ChangeDescription 名。这些元素是那些已经删除但是在回滚时需要放回图中的数据对象,这就是变更的 EMF 视图。总而言之, objectsToAttach == deleted objects(删除的对象)
。
|
调试应用程序
如果在示例应用程序中将 debug
变量设为 true
,那么就可以执行以下调用,查看数据图的序列化版本。
|
还可以使用 Eclipse 调试环境。比如,我们建议在 SDOClient.java 第 100 行设置一个断点,并调试 SDOClient
(像在 Java 应用程序中那样)。然后,在调试窗口中就可以看到内存中的数据图(在 Variables 下)和它的数据对象(Boss、Terrence Shorter 等),如图 4 所示。
还可以用这种方法查看变更摘要,如图 5 所示。
上面的截屏图看起来很复杂,现在看来用处也不大,但是当您调试自己的 SDO 应用程序并查看数据对象和变更摘要的内容时,就会用到它。
|
结束语
本文简要描述了 SDO 及其功能。我们通过一个示例应用程序说明了 SDO 的一些功能。更多参考信息,请参阅 Eclipse 帮助系统下的 SDO API 文档。该规范仍在发展和改进之中。比如,SDO 1.0 强调了 SDO 的客户机方面,而没有指定一个 DMS API。SDO 目前正通过 JCP 实现标准化,请关注有关的进展情况。因为 SDO 非常灵活,设计 SDO 应用程序时需要做很多决策。这些决策将影响程序的重用性和性能。因此,在编码之前,应该认真考虑应用模式和应用程序数据的特征。