服务数据对象(Service Data Objects,SDO) 是BEA 和 IBM 最近共同发布的一项规范,而且它正由JSR-235专家组进行标准化以通过JCP(Java 标准化组织)的审核。SDO是Java平台的一种数据编程架构和API,它统一了不同数据源类型的数据编程,提供了对通用应用程序模式的健壮支持,并使应用程序、工具和框架更容易查询、读取、更新和检查数据。关于SDO架构的高级概述,请参考白皮书Next-Generation Data Programming: Service Data Objects (pdf)。
这篇文章讨论了SDO API背后的设计和动机。注意,在这篇文章中讨论的SDO API是由 BEA和IBM在2003年11月共同发布的版本。由JSR-235专家组推出的版本可能会不同于这个API。
SDO API包括一个动态数据API,一个数据类型自检API,和一个数据变化跟踪API。如果您一点也不知道上述概念,不要绝望:我将从最简单的概念开始并逐步进行下去。
DataObject:当不采用Java Beans时
SDO基于“数据对象”的概念,“数据对象”简单地说就是包含某个数据的一个对象实例。通常,人们使用传统的Java对象(POJO,或Java beans)或是传统的Java接口(POJI)来以一种持久性-机制-中立的风格表示数据(不久将更多地用于关系型和XML数据上)。举例来说,人们为了使用 POJO 普遍会构造“数据传输对象”。
我们称Java bean类型的API为“静态的”,因为预先定义好的具有一系列属性(或getter/setter方法)的数据类型已经存在了。然而,静态数据API并不总能执行,因为有时Java类甚至还并不存在。举例来说,在许多动态查询中,返回数据的形式并不是已知的预先类型,这样我们就不能将数据填写到已经存在的Java类中。另一个例子是,数据结构是可扩展的;例如,对于XML数据,在您剖析它之前,您通常不知道它的精确类型(假定它的XML 模式结构是可扩展的)。
这就是 SDO 数据对象接口的便利之处:它提供了“动态的”数据API。当您需要产生一个能支持包括动态查询、未知数据类型和可括展模式等情况的通用框架时,有一个动态的数据API会更加有用。
DataObject 上的基本操作是set([property name]、[property value])和get([property name]。在 DataObject 上有更多的方法,但是我们稍后将了解它们。让我们先来看一些代码。假定我有一个person接口,如下:
public interface Person { String getName(); void setName(String name); } |
模拟的客户代码如下(假定实现person接口的一个PersonImpl Java bean存在):
Person p = new PersonImpl(); p.setName("John"); System.out.println(p.getName()); |
这是Java程序师在许多程序设计任务中使用的。但是,如果在运行时当我要处理person数据的时候,person接口却不存在,那会怎么样呢?这时我们就能使用DataObject。假定有一个DataObject的实现DataObjectImpl,它有一个默认的构造函数(注意,SDO规范的内核只定义接口),我可以写出下列代码,它同上面的代码可以完成同样的事情:
DataObject o = new DataObjectImpl(); o.set("name", "John"); System.out.println(o.get("name")); |
留心的读者将会注意到,上面的代码中遗漏了一个重要的数据:DataObject的实例并不知道这个数据是person。这样,客户也不能做任何运行时的类型检查。SDO处理了这个问题,下面我们就来讨论它。
退一步讲,指出Java对 DataObject API的需求有所增加是有意义的。因为Java是一种静态类型的语言,它不能在运行时将额外的字段和方法添加到对象的实例中去。也不是所有的语言都像这样。特别地,如Python,它是一种动态类型的语言,它允许属性 (等同于Java中的字段) 在运行时被添加到一个对象实例中。举个例子,下面的Python代码就粗略地等同于上面使用DataObject的Java代码。
o = dataobject() setattr(o, "name", "John") print o.name |
上面的代码假定已经定义了名为“dataobject”的Python类。这个类可以简单地定义成:
class dataobject: pass |
我这里给出Python代码并不是要在Python和Java之间做何评价;只不过,它更明确地说明了当需要一个动态数据API时,为什么Java需要像 DataObject 这样的类。
进一步解释
DataObject不只提供了set()和get()这两种简单获取和返回java.lang.Object的方法。在通常的 POJO或POJI中,访问程序要指定更特定的类型。也就是,它们处理string、int、calendar等,而不是对象。DataObject接口允许它的属性也被赋予类型,并且为此提供了附加的getter和setter。这与java.lang.reflect.Field提供的getter和setter相似。例如,在对象的例子中,我可以按如下来做:
DataObject o = new DataObjectImpl(); o.setString("name", "John"); System.out.println(o.getString("name")); |
当然,在这里更改以上代码中的最后一行并非是必需的。但如果需要作为一个字符串在返回的对象上操作,则需要显式的 getString()。
Java的其他动态数据API
在出现其他DataObject之前,Java还有其他动态数据API。主要地讲,JDBC ResultSet和RowSet API是用于关系数据的动态数据API,而DOM API(尤其是节点和元素)是用于XML数据的动态数据API。使用这些API的对等代码大致如下(为了简便起见,我将显示get而忽略set):
使用RowSet API:
RowSet rs = ...; System.out.println(rs.getString("name")); And using the DOM API: Node n = ...; // find the name node System.out.println(n.getNodeValue()); |
这里的突出点在于,SDO的DataObject接口是一个泛化的动态数据API,这意味着它可以独立于任何特定的持久性机制或串行化格式而被使用。人们将它设计得可良好处理对象数据、关系数据、表列数据和XML。它允许更高级别的框架处理来自多个异构数据源的数据。
类型和特性
如POJO和POJI等静态类型的数据API将所有类型信息硬编码到其中。接口或类定义了类型,而且这一类型具有用静态类型的getter和setter访问的特性。同时,这些类型可以用java.lang.Class和java.lang.reflect API来内省。这些都可以用于多个东西,从简单的运行时类型测试到使泛化框架在您的Java对象上操作。在Java类和接口不存在的情况下,很明显不能采用这种方式。我们在SDO需要一个等同与java.lang.Class和java.lang.reflect.Field的东西。这正是类型和特性接口所扮演的角色。类型大约等同于类,而属性大约等同于字段。
让我们看一些代码。在person接口的情况下,我可以如下利用类型信息:
Object o = ...; // set o to some Person object if (o instanceof Person) { Person p = (Person) o; System.out.println(p.getName()); } |
使用SDP API,则可以如下编写代码:
DataObject o = ...; // set o to some DataObject Type t = o.getType(); if (t.getName() == "Person") { System.out.println(o.getString("name")); } |
注意,为了上面的代码正确工作,类型信息必须来自某个地方。您将注意到,在上面的代码中,我假设有一个简单的未用任何参数构造的DataObjectImpl对象——如果您希望创建一个带有一个类型的DataObject,就需要改变这种状况。您可能会问:为什么SDO要定义具体的类来从零构建SDO。这可以在最后来完成。我们首先将重点放在提供一个可以消费SDO的客户端API上,同时假定支持SDO的产品暂时将以一种专有的方式来构造SDO。这将随着时间的推移而随时更改。
除了询问DataObject的类型外,还可以询问它的Property对象集。这使得各个工具可以遍历DataObject的图。下面的代码就是这样的一个工具;它漂亮地打印出(pretty-print)一个DataObject及其特性:
public void printDataObject(DataObject dataObject, int indent) { Type type = dataObject.getType(); List properties = type.getProperties(); for (int p=0, size=properties.size(); p < size; p++) { if (dataObject.isSet(p)) { Property property = (Property) properties.get(p); // For many-valued properties, process a list of values if (property.isMany()) { List values = dataObject.getList(p); for (int v=0; count=values.size(); v < count; v++) { printValue(values.get(v), property, indent); } else { // For single-valued properties, print out the value printValue(dataObject.get(p), property, indent); } } } } private void printValue(Object value, Property property, int indent) { // Get the name of the property String propertyName = property.getName(); // Construct a string for the proper indentation String margin = ""; for (int i = 0; i < indent; i++) margin += "/t"; if (!property.isContainment()) { // For non-containment properties, just print the value System.out.println(margin + propertyName + ": " + value); } else { // For containment properties, display the value with printDataObject String typeName = property.getType().getName(); System.out.println(margin + propertyName + " (" +typeName+ "):"); printDataObject((DataObject) value, indent + 1); } } |
从上边的代码可以注意到,SDO API存在一些我们尚未讨论的复杂细节。详细信息请查看SDO规范。
Java bean和SDO联姻
表示数据的预定义POJO和POJI比DataObject接口更易于共用。如前面所指出的,这并不总是可能的。如果我需要编写一个能够处理最小公分母的框架,该框架应该能够和DataObject共用。但我们并不希望将我们的Java bean用户晾在一边。那么我们能做些什么呢?这里有一些可行的策略。
让我们拿JAXB做例子。一个兼容JAXB的工具可以从XML 模式定义(XSD)生成POJI,而这个工具还将生成“Impl”类来实现这些接口。一个人可以设想一个JAXB 模式编译器是支持SDO的,Impl类也由此而实现DataObject接口。因此,这些框架可以带有一个JAXB生成的对象并成功地向下转型(downcast)到DataObject。
变更跟踪
SDO有很好的记忆能力:它们可以记住过去发生了什么,并告诉您什么发生了变化。这一点为何很有用呢?一个非常常见的访问模式是:一个客户端(如一个Web应用程序)接收到来自一个数据源的信息,更改该信息的一部分,然后使数据源提交这些更改。在SOD架构中,我们将负责答复查询和提交更改的服务称为一个“中介”(mediator)。这个中介将一个数据源做“前端”来完成上述任务。因此,您可以具有SQL/关系中介、命中XML数据仓库的XML查询中介及其所有种类的组合。为了使中介完成其将更改提交给一个DataObject的任务,它需要能询问DataObject什么已经被更改。
DataGraph界面提供了一个方便的机制来传递一组数据。DataGraph接口提供了getChangeSummary()方法,它退还一个ChangeSummary对象。从这个对象中,您可以得到一个已被更改的数据对象和哪些是新值的列表。还有一种额外的方法getOldValues()可告诉您一个DataObject的旧值是什么。
结束语
本文讨论了SDO API的基础知识。还有很多我们没有涉及到,如使用XPath表达式进行特性访问、对XML样式混合内容的支持、用于DataGraph的XML串行化形式以及其他特性。SDO 规范 (.pdf) 涵盖了这些主题。请您注意BEA和IBM即将推出的支持SDO的产品,以及JSR-235上的产品,它将在 JCP 内对SDO API进行标准化。