用于实现 Web 服务的 SOA 编程模型,第 2 部分: 使用服务数据对象简化的数据访问

用于实现 Web 服务的 SOA 编程模型,第 2 部分: 使用服务数据对象简化的数据访问

利用服务数据对象简化面向服务的软件中的数据访问和表示。SDO 用统一的抽象代替各种各样的数据访问模型来创建、检索、更新和删除供服务实现使用的业务数据。这是我们有关用于 IBM® 面向服务的体系结构 (SOA) 的编程模型系列文章的第二篇。

服务对象数据

服务对象数据(Service Data Object,SDO)使用统一的抽象代替了各种各样的数据访问模型来创建、检索、更新和删除供服务实现使用的业务数据。SDO(请参阅参考资料部分中的 Service Data Objects 2.0 和 Next-Generation Data Programming: Service Data Objects)是 IBM 面向服务的体系结构 (SOA) 的基础概念。SDO 将开发人员从如何访问特定的后端数据源的技术细节中解放出来,提高了他们的工作效率,这样他们就可以主要专注于业务逻辑(请参阅参考资料部分中的 Integrating relational data into Web applications、Next-generation data programming in the Java™ environment 以及 Using Service Data Objects with Enterprise Information Integration technology)。SDO 是与 BEA Systems, Inc. 联合制订的规范,并且在 IBM 系列产品中得到了广泛的使用,包括 WebSphere® Application Server 和 Rational® Studio 工具。Java™ 数据库连接(Java™ DataBase Connectivity),通常称为 JDBC,是执行结构化查询语言(Structured Query Langauge,SQL)语句的 Java 接口。目前,用于 JDBC、Web 服务描述语言(Web Services Description Language,WSDL)定义的服务、企业 JavaBean(Enterprise JavaBean,EJB)等等由 Java 编写的服务实现的编程模型都是相似的,但却又有一些让人讨厌的不同。

SDO 定义了一种单一的、统一的方法来访问和操作来自异构数据源的数据,包括关系型数据库、可扩展标记语言(eXtensible Markup Language,XML)数据源、Web 服务以及企业信息系统 (EIS)。它们是基于数据图(data graph)的概念。数据图就是一组可以从数据源中分离出来的树形结构的对象。SDO 可以在整个应用程序体系结构中使用。

应用程序体系结构的领域 如何使用 SDO
SOA
  • SDO 是服务的输入和输出。
数据访问
  • SDO 访问关系型、XML、EJB、Java 数据对象(Java Data Object,JDO)和 Hibernate 数据源。
  • SDO 是数据传输对象(Data Transfer Object,DTO)——也被称为值对象(Value Object)。
Web 服务
  • SDO 表示网络上的 XML。
消息传递
  • SDO 表示消息。
XML 使用 SDO 的情况:
  • 支持 XML 的应用程序。
  • 访问 XML 文件、文档、资源和消息。
连接器/适配器(EIS,CICS)
  • SDO 表示数据记录。
EJB
  • SDO 是 DTO(也被称作值对象)。
  • Java 2 企业版(Java 2 Enterprise Edition,J2EE)设计模式。
ADO.NET
  • ADO DataSet 是 SDO 数据图的子集。
企业服务总线(Enterprise Service Bus,ESB)
  • SDO 是服务的输入和输出。
跨语言编程模型
  • 完整的应用程序可能横跨层和语言。
  • 用于很多种语言技能集的相同的编程模型。
模型驱动的体系结构(Model-driven architecture,MDA)
  • SDO 模型(类型(Type)和属性(Property))是通过统一建模语言(Unified Modelling Language,UML)类和组件定义的。
  • SDO 应用程序遵循 UML 顺序 (Sequence)、流 (Flow)、状态 (State) 和协作 (Collaboration)。
Java
  • SDO 是带有 POJO 接口的智能的“传统 Java 对象(plain old Java object,POJO)”。

在 SOA 中,应用程序并不直接地连接数据源。它访问一个叫做数据访问服务(data access service,DAS)的中介并接收响应中的数据图。DAS 是为特定数据源种类处理技术细节的服务。它为客户机将数据转换成 SDO 图。客户机应用程序与数据图进行交互来获得数据和改变数据。为了将更新应用于原始的数据源,应用程序将更新过的图发送回 DAS,而 DAS 又与数据源交互。通常,运行时提供 DAS 的实现,而应用程序开发工具提供对数据图的支持。

SDO 通过封装数据访问的细节将业务应用程序与技术改变相隔离,从而避开了技术改变产生的影响——重新编写应用程序以便跟上改变的技术(请参阅参考资料部分中的 Wikipedia)。例如,考虑一个设计用来从数据库中读取产品描述并将其作为网页显示的 Java Web 应用程序。为了访问数据库中的产品描述,应用程序很可能使用 JDBC。假设不久后应用程序拓扑发生了改变,在应用程序和数据库之间放置了 Web 服务。现在,应用程序不能再使用 JDBC 访问数据,而是需要重做大量的工作来替换 Web 服务应用编程接口 (API),例如文档对象模型(Document Object Model,DOM)或者基于 XML 的远程过程调用的 Java API(Java APIs for XML-Based Remote Procedure Call,JAX-RPC)。SDO 避免了这个问题;使用 SDO 编写的应用程序不必改变。

另外,SDO 提供了支持元数据 API 的应用程序、工具和框架来以统一的方式自省数据模型,而不管它的来源。DAS 将后端元数据转换成标准的 SDO 格式。

SDO 类型可以由 Java 接口、XML Schema、关系型表和列、EJB、COBOL 记录、消息或者 UML 来定义(请参阅参考资料部分中的 Catalog of OMG Modeling and Metadata Specifications);实现人员可以选择自己喜欢的系统类型。简单 Java 和 XML 数据类型是有效的 SDO 数据类型,这为 Java 实现人员简化了一步。SDO 支持动态的和静态的数据访问模型,两者也可以一起使用。我们将更详细地考虑这些内容:

  • 动态模型(缺省值)允许编程人员通过名称(字符串)获得和设置数据图中的数据元素。当 SDO 的类型在编译阶段未知时,或者当程序部署完以后可能要添加新的属性时,这特别有用。客户机应用程序或服务查询 SDO 来了解它的结构,然后按名称读取和更新任何元素。例如,可以编写一个泛型 SDO 访问函数并用特定于元素的元数据填充它来访问单独的 SDO。SDO 同样也使用 XML 路径语言( XML Path Language,XPath)表达式的子集来支持快速遍历许多 DataObject,例如 customer[1]/address/zip,以便快速访问 customer DataObejct 的 zip 代码。
  • 静态模型使用命名和类型化 Java 接口。每个数据元素有自己的“getter”和“setter”方法。工具从 SDO 类型和属性生成静态接口。

SDO 对于数据表示非常重要,即使没有典型的数据源也如此。这种用法的例子包括使用 Web 服务交换的 XML 消息、Java 消息服务 (JMS) 消息、XML 文件等等。







示例

下面的例子——定义了包含客户数据的数据对象——说明了使用 Java 或 XML 来定义和使用 SDO 是多么的容易。示例 1(使用 XML)是 SDO 类型的基础。


示例 1. 使用 XML 的 SDO 类型定义
												
														 <?xml version="1.0" encoding="UTF-8"?> <schema xmlns="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://www.myvalue.com" targetNamespace="http://www.myvalue.com"> <element name="customer" type="Customer"/> <complexType name="Customer"> <sequence> <element name="customerID" type="string"/> <element name="firstName" type="string"/> <element name="lastName" type="string"/> <element name="stockSymbol" type="string"/> <element name="stockQuantity" type="int"/> </sequence> </complexType> </schema> 
												
										

在示例 2 中,由前面的 XML 所生成的 Java 接口说明了如何使用静态接口。


示例 2. 使用 Java 的 SDO 类型定义
												
														 public interface Customer { String getCustomerID(); void setCustomerID(String customerID); String getFirstName(); void setFirstName(String firstName); String getLastName(); void setLastName(String lastName); String getStockSymbol(); void setStockSymbol(String stockSymbol); int getStockQuantity(); void setStockQuantity(int stockQuantity); } 
												
										

一旦 SDO 类型定义好,就可以通过将类型定义传递给 SDO 数据工厂 (data factory) 来将其实例化(为数据对象分配存储空间)。这个工厂仅仅是运行时的一个组件,它的功能就是根据 SDO 类型定义实例化 SDO 数据对象。

示例 3 和示例 4 分别展示了如何通过传递 XML Schema 名称空间和复杂的类型名称(在示例 1 中定义的)或者 Java 接口类(在示例 2 中定义的)作为参数来创建 SDO。


示例 3. 使用 XML Schema 名称空间和复杂的类型名称参数创建 SDO
												
														 DataObject customer = DataFactory.INSTANCE.create("http://www.myvalue.com", "Customer"); 
												
										



示例 4. 使用具有 Java 接口类参数的 SDO DataFactory 创建 SDO
												
														 Customer customer = (Customer) DataFactory.INSTANCE.create(Customer.class); 
												
										

在 SDO 实例化之后,实现就可以访问它了。示例 5 和示例 6 中的代码示例分别展示了对 Customer SDO 的动态和静态访问。


示例 5. 动态访问 SDO
												
														 DataObject customer = ... ; customer.setString("customerID", customerID); ... customer.setInt("stockQuantity", 100); String customerID = customer.getString("customerID"); ... int stockQuantity = customer.getInt("stockQuantity"); 
												
										



示例 6. 静态访问 SDO
												
														 Customer customer = ... ; customer.setCustomerID(customerID); ... customer.setStockQuantity(100); String customerID = customer.getCustomerID(); ... int stockQuantity = customer.getStockQuantity(); 
												
										

我们使用访问 XML 文件服务和关系数据库的例子来进一步说明由 SDO 推动的编程模型的简单性。请注意,尽管技术之间存在差异,但是这些应用程序是如此的相似。应用程序开发人员能够专注于业务逻辑,而让服务去处理更新持久数据存储的实现细节。

示例 7. XML 文件服务

这个简单的例子将数据从 XML 文件加载到 SDO 数据图,打印并更新数据,然后将它写回文件。(业务目标是将“quot;Adam”改为“Kevin”。)

根据根 XML 元素和一个多值的 customer 属性定义将要作为根 customers 数据对象读入的 XML 文件。在 XML 文件中 Customers 为每个 customer 元素包含一个数据对象。每个 customer 具有两个属性:SNfirstName
																				
																						<customers xmlns="http://customers.com"> <customer SN="1" firstName="Adam" /> <customer SN="2" firstName="Baker" /> </customers> 
																				
																		

读取文件数据。
																				
																						DataObject root = xmlService.load(InputStream); 
																				
																		

遍历 customer 数据对象列表并打印出每个名 (first name)。
																				
																						Iterator i = root.getList("customer").iterator(); while (i.hasNext()) { DataObject cust = (DataObject) i.next(); String name = cust.getString("firstName"); System.out.println(name); } 
																				
																		

将第一个客户数据对象的 firstName 属性设为 Kevin。中间件更新变更摘要(这里并没有显示出来)来指示改变了的数据。
																				
																						DataObject customer1 = root.getDataObject("customer[1]"); customer1.setString("firstName", "Kevin");  // or root.setString("customer[1]/firstName", "Kevin"); 
																				
																		

将数据对象写入文件。
																				
																						xmlService.save(OutputStream, root); 
																				
																		

结果是更新的 XML 文档。
																				
																						<customers xmlns="http://customers.com"> <customer SN="1" firstName="Kevin" /> <customer SN="2" firstName="Baker" /> </customers> 
																				
																		

示例 8. 访问关系数据库

虽然复杂的关系数据库到 SDO 的映射是可行的,但是这个例子使用的是一个非常简单的映射:每个数据库表都是一个 SDO 类型,表的每行是 SDO 数据对象,而每列是 SDO 属性。应用程序逻辑是相同的:通过执行预先定义好的查询从数据库中读取、打印并更新数据(将“Adam”改为“Kevin”),将更改保存到数据库。数据库查询返回 CUSTOMER 表中的两行:

CUSTOMER ID(整数,主键) CUSTOMER FIRSTNAME(字符串) CUSTOMER LASTNAME(字符串)
1 Adam Smith
2 Baker Street

下面给出了带有解释的 SDO 实现。

rdbService 查询数据库以获得数据。
																				
																						DataObject root = rdbService.get(); 
																				
																		

相同的数据可能已经用 XML 等价地表示了。
																				
																						<customers> <CUSTOMER ID="1" FIRSTNAME="Adam" LASTNAME="Smith"/> <CUSTOMER ID="2" FIRSTNAME="Baker" LASTNAME="Street"/> </customers> 
																				
																		

打印每个客户名。
																				
																						Iterator i = root.getList("CUSTOMER").iterator(); while (i.hasNext()) { DataObject cust = (DataObject) i.next(); String name = cust.getString("FIRSTNAME"); System.out.println(name); } 
																				
																		

将第一个数据对象的 FIRSTNAME 设为 Kevin。中间件更新变更摘要(这里并没有显示出来)来指示改变。
																				
																						DataObject customer1 = root.getDataObject("CUSTOMER[1]"); customer1.setString("FIRSTNAME", "Kevin"); // or root.setString("CUSTOMER[1]/FIRSTNAME", "Kevin"); 
																				
																		

将更新的数据写入数据库。
																				
																						rdbService.update(root); 
																				
																		

现在数据库包含:

CUSTOMER ID(整数,主键) CUSTOMER FIRSTNAME(字符串) CUSTOMER LASTNAME(字符串)
1 Kevin Smith
2 Baker Street

注意,第一行已经被更新了。

如果在我们的示例应用程序已经获得数据图之后,另外一个应用程序访问数据库并更改了值会怎么样?在写入时,数据访问服务检查变更摘要来决定如何对数据源应用更新。数据库可以使用开放式并发控制 (optimistic concurrency control) 来确保这个改变之前最后包含的值是“Adam”(否则,另外一个应用程序可能先改变数据,可能在该应用程序中需要某些错误恢复)。某些服务实现了更为高级的开放式并发形式;变更历史记录提供了那些算法所需要的原始值。

使用 EJB 时,SDO 作为 DTO(也称作值对象)J2EE 设计模式。一般来说,访问实体 EJB(Entity EJB)的每个属性的开销非常大,所以传输几个数据图中的 SDO 对象效率更高。会话 EJB(Session EJB)可能有方法产生和使用 SDO 图来更加高效地直接访问实体 EJB。Customer 实体 EJB 封装了对 Customer 记录的数据库访问。会话 EJB 提供了访问方法来从 Customer 实体 EJB 产生和返回 Customer SDO 图。


示例 9. 返回 SDO 和由 SDO 更新实体 EJB 的会话 Bean 接口
												
														 public interface CustomerSession { Customer getCustomerByID(String customerID); Customers getCustomersByLastName(String lastName); void updateCustomers(Customers customers); } 
												
										

Customers 是包含若干客户的 SDO 根数据对象。CustomersList 包含 Customer 数据对象。ChangeSummary 用来记录对 Customer 对象所做的全部更改,包括任何的添加、删除或更新。updateCustomers() 方法利用所做的更改来更新 Customer 实体 EJB,并且可以在一个事务中批处理对数据源的更改。


示例 10. 使用 Java 的 Customer 的 SDO 类型定义
												
														 public interface Customers { List<Customer> getCustomers(); ChangeSummary getChanges(); } 
												
										







总结

SDO 为所有数据源启用了对应用程序数据的统一访问和公共编程模型,而不管数据存储在何地以及如何存储。SDO 利用了 XML 的简易性,而又没有引入 XML Schema 的复杂性或序列化的性能问题。通过同时使用 SDO 和 SOA,可以将系统编程任务从业务逻辑中分离出来,并且将其封装在可重用的服务之中,而不是所有编程人员都必须掌握这些技能才能入门。它们在没有陷入技术和实现细节的情况下简化了业务应用程序的编程,防止了技术改变产生的影响。有了 SDO,业务应用程序就是名副其实的业务应用程序。

你可能感兴趣的:(用于实现 Web 服务的 SOA 编程模型,第 2 部分: 使用服务数据对象简化的数据访问)