利用服務(wù)數(shù)據(jù)對象簡化面向服務(wù)的軟件中的數(shù)據(jù)訪問和表示。SDO 用統(tǒng)一的抽象代替各種各樣的數(shù)據(jù)訪問模型來創(chuàng)建、檢索、更新和刪除供服務(wù)實現(xiàn)使用的業(yè)務(wù)數(shù)據(jù)。這是我們有關(guān)用于 IBM? 面向服務(wù)的體系結(jié)構(gòu) (SOA) 的編程模型系列文章的第二篇。
服務(wù)對象數(shù)據(jù)
服務(wù)對象數(shù)據(jù)(Service Data Object,SDO)使用統(tǒng)一的抽象代替了各種各樣的數(shù)據(jù)訪問模型來創(chuàng)建、檢索、更新和刪除供服務(wù)實現(xiàn)使用的業(yè)務(wù)數(shù)據(jù)。SDO(請參閱參考資料部分中的 Service Data Objects 2.0 和 Next-Generation Data Programming: Service Data Objects)是 IBM 面向服務(wù)的體系結(jié)構(gòu) (SOA) 的基礎(chǔ)概念。SDO 將開發(fā)人員從如何訪問特定的后端數(shù)據(jù)源的技術(shù)細(xì)節(jié)中解放出來,提高了他們的工作效率,這樣他們就可以主要專注于業(yè)務(wù)邏輯(請參閱參考資料部分中的 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. 聯(lián)合制訂的規(guī)范,并且在 IBM 系列產(chǎn)品中得到了廣泛的使用,包括 WebSphere? Application Server 和 Rational? Studio 工具。Java? 數(shù)據(jù)庫連接(Java? DataBase Connectivity),通常稱為 JDBC,是執(zhí)行結(jié)構(gòu)化查詢語言(Structured Query Langauge,SQL)語句的 Java 接口。目前,用于 JDBC、Web 服務(wù)描述語言(Web Services Description Language,WSDL)定義的服務(wù)、企業(yè) JavaBean(Enterprise JavaBean,EJB)等等由 Java 編寫的服務(wù)實現(xiàn)的編程模型都是相似的,但卻又有一些讓人討厭的不同。
SDO 定義了一種單一的、統(tǒng)一的方法來訪問和操作來自異構(gòu)數(shù)據(jù)源的數(shù)據(jù),包括關(guān)系型數(shù)據(jù)庫、可擴展標(biāo)記語言(eXtensible Markup Language,XML)數(shù)據(jù)源、Web 服務(wù)以及企業(yè)信息系統(tǒng) (EIS)。它們是基于數(shù)據(jù)圖(data graph)的概念。數(shù)據(jù)圖就是一組可以從數(shù)據(jù)源中分離出來的樹形結(jié)構(gòu)的對象。SDO 可以在整個應(yīng)用程序體系結(jié)構(gòu)中使用。
應(yīng)用程序體系結(jié)構(gòu)的領(lǐng)域
|
如何使用 SDO
|
SOA |
|
數(shù)據(jù)訪問 |
- SDO 訪問關(guān)系型、XML、EJB、Java 數(shù)據(jù)對象(Java Data Object,JDO)和 Hibernate 數(shù)據(jù)源。
- SDO 是數(shù)據(jù)傳輸對象(Data Transfer Object,DTO)——也被稱為值對象(Value Object)。
|
Web 服務(wù) |
- SDO 表示網(wǎng)絡(luò)上的 XML。
|
消息傳遞 |
|
XML |
使用 SDO 的情況:
- 支持 XML 的應(yīng)用程序。
- 訪問 XML 文件、文檔、資源和消息。
|
連接器/適配器(EIS,CICS) |
|
EJB |
- SDO 是 DTO(也被稱作值對象)。
- Java 2 企業(yè)版(Java 2 Enterprise Edition,J2EE)設(shè)計模式。
|
ADO.NET |
- ADO DataSet 是 SDO 數(shù)據(jù)圖的子集。
|
企業(yè)服務(wù)總線(Enterprise Service Bus,ESB) |
|
跨語言編程模型 |
- 完整的應(yīng)用程序可能橫跨層和語言。
- 用于很多種語言技能集的相同的編程模型。
|
模型驅(qū)動的體系結(jié)構(gòu)(Model-driven architecture,MDA) |
- SDO 模型(類型(Type)和屬性(Property))是通過統(tǒng)一建模語言(Unified Modelling Language,UML)類和組件定義的。
- SDO 應(yīng)用程序遵循 UML 順序 (Sequence)、流 (Flow)、狀態(tài) (State) 和協(xié)作 (Collaboration)。
|
Java |
- SDO 是帶有 POJO 接口的智能的“傳統(tǒng) Java 對象(plain old Java object,POJO)”。
|
在 SOA 中,應(yīng)用程序并不直接地連接數(shù)據(jù)源。它訪問一個叫做數(shù)據(jù)訪問服務(wù)(data access service,DAS)的中介并接收響應(yīng)中的數(shù)據(jù)圖。DAS 是為特定數(shù)據(jù)源種類處理技術(shù)細(xì)節(jié)的服務(wù)。它為客戶機將數(shù)據(jù)轉(zhuǎn)換成 SDO 圖??蛻魴C應(yīng)用程序與數(shù)據(jù)圖進(jìn)行交互來獲得數(shù)據(jù)和改變數(shù)據(jù)。為了將更新應(yīng)用于原始的數(shù)據(jù)源,應(yīng)用程序?qū)⒏逻^的圖發(fā)送回 DAS,而 DAS 又與數(shù)據(jù)源交互。通常,運行時提供 DAS 的實現(xiàn),而應(yīng)用程序開發(fā)工具提供對數(shù)據(jù)圖的支持。
SDO 通過封裝數(shù)據(jù)訪問的細(xì)節(jié)將業(yè)務(wù)應(yīng)用程序與技術(shù)改變相隔離,從而避開了技術(shù)改變產(chǎn)生的影響——重新編寫應(yīng)用程序以便跟上改變的技術(shù)(請參閱參考資料部分中的 Wikipedia)。例如,考慮一個設(shè)計用來從數(shù)據(jù)庫中讀取產(chǎn)品描述并將其作為網(wǎng)頁顯示的 Java Web 應(yīng)用程序。為了訪問數(shù)據(jù)庫中的產(chǎn)品描述,應(yīng)用程序很可能使用 JDBC。假設(shè)不久后應(yīng)用程序拓?fù)浒l(fā)生了改變,在應(yīng)用程序和數(shù)據(jù)庫之間放置了 Web 服務(wù)?,F(xiàn)在,應(yīng)用程序不能再使用 JDBC 訪問數(shù)據(jù),而是需要重做大量的工作來替換 Web 服務(wù)應(yīng)用編程接口 (API),例如文檔對象模型(Document Object Model,DOM)或者基于 XML 的遠(yuǎn)程過程調(diào)用的 Java API(Java APIs for XML-Based Remote Procedure Call,JAX-RPC)。SDO 避免了這個問題;使用 SDO 編寫的應(yīng)用程序不必改變。
另外,SDO 提供了支持元數(shù)據(jù) API 的應(yīng)用程序、工具和框架來以統(tǒng)一的方式自省數(shù)據(jù)模型,而不管它的來源。DAS 將后端元數(shù)據(jù)轉(zhuǎn)換成標(biāo)準(zhǔn)的 SDO 格式。
SDO 類型可以由 Java 接口、XML Schema、關(guān)系型表和列、EJB、COBOL 記錄、消息或者 UML 來定義(請參閱參考資料部分中的 Catalog of OMG Modeling and Metadata Specifications);實現(xiàn)人員可以選擇自己喜歡的系統(tǒng)類型。簡單 Java 和 XML 數(shù)據(jù)類型是有效的 SDO 數(shù)據(jù)類型,這為 Java 實現(xiàn)人員簡化了一步。SDO 支持動態(tài)的和靜態(tài)的數(shù)據(jù)訪問模型,兩者也可以一起使用。我們將更詳細(xì)地考慮這些內(nèi)容:
- 動態(tài)模型(缺省值)允許編程人員通過名稱(字符串)獲得和設(shè)置數(shù)據(jù)圖中的數(shù)據(jù)元素。當(dāng) SDO 的類型在編譯階段未知時,或者當(dāng)程序部署完以后可能要添加新的屬性時,這特別有用??蛻魴C應(yīng)用程序或服務(wù)查詢 SDO 來了解它的結(jié)構(gòu),然后按名稱讀取和更新任何元素。例如,可以編寫一個泛型 SDO 訪問函數(shù)并用特定于元素的元數(shù)據(jù)填充它來訪問單獨的 SDO。SDO 同樣也使用 XML 路徑語言( XML Path Language,XPath)表達(dá)式的子集來支持快速遍歷許多 DataObject,例如
customer[1]/address/zip
,以便快速訪問 customer
DataObejct 的 zip 代碼。
- 靜態(tài)模型使用命名和類型化 Java 接口。每個數(shù)據(jù)元素有自己的“getter”和“setter”方法。工具從 SDO 類型和屬性生成靜態(tài)接口。
SDO 對于數(shù)據(jù)表示非常重要,即使沒有典型的數(shù)據(jù)源也如此。這種用法的例子包括使用 Web 服務(wù)交換的 XML 消息、Java 消息服務(wù) (JMS) 消息、XML 文件等等。
示例
下面的例子——定義了包含客戶數(shù)據(jù)的數(shù)據(jù)對象——說明了使用 Java 或 XML 來定義和使用 SDO 是多么的容易。示例 1(使用 XML)是 SDO 類型的基礎(chǔ)。
示例 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 接口說明了如何使用靜態(tài)接口。
示例 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 數(shù)據(jù)工廠 (data factory) 來將其實例化(為數(shù)據(jù)對象分配存儲空間)。這個工廠僅僅是運行時的一個組件,它的功能就是根據(jù) SDO 類型定義實例化 SDO 數(shù)據(jù)對象。
示例 3
和示例 4 分別展示了如何通過傳遞 XML Schema 名稱空間和復(fù)雜的類型名稱(在示例 1 中定義的)或者 Java 接口類(在示例 2 中定義的)作為參數(shù)來創(chuàng)建 SDO。
示例 3. 使用 XML Schema 名稱空間和復(fù)雜的類型名稱參數(shù)創(chuàng)建 SDO
DataObject customer = DataFactory.INSTANCE.create("http://www.myvalue.com", "Customer");
|
示例 4. 使用具有 Java 接口類參數(shù)的 SDO DataFactory 創(chuàng)建 SDO
Customer customer = (Customer) DataFactory.INSTANCE.create(Customer.class);
|
在 SDO 實例化之后,實現(xiàn)就可以訪問它了。示例 5 和示例 6 中的代碼示例分別展示了對 Customer SDO 的動態(tài)和靜態(tài)訪問。
示例 5. 動態(tài)訪問 SDO
DataObject customer = ... ;
customer.setString("customerID", customerID);
...
customer.setInt("stockQuantity", 100);
String customerID = customer.getString("customerID");
...
int stockQuantity = customer.getInt("stockQuantity");
|
示例 6. 靜態(tài)訪問 SDO
Customer customer = ... ;
customer.setCustomerID(customerID);
...
customer.setStockQuantity(100);
String customerID = customer.getCustomerID();
...
int stockQuantity = customer.getStockQuantity();
|
我們使用訪問 XML 文件服務(wù)和關(guān)系數(shù)據(jù)庫的例子來進(jìn)一步說明由 SDO 推動的編程模型的簡單性。請注意,盡管技術(shù)之間存在差異,但是這些應(yīng)用程序是如此的相似。應(yīng)用程序開發(fā)人員能夠?qū)W⒂跇I(yè)務(wù)邏輯,而讓服務(wù)去處理更新持久數(shù)據(jù)存儲的實現(xiàn)細(xì)節(jié)。
示例 7. XML 文件服務(wù)
這個簡單的例子將數(shù)據(jù)從 XML 文件加載到 SDO 數(shù)據(jù)圖,打印并更新數(shù)據(jù),然后將它寫回文件。(業(yè)務(wù)目標(biāo)是將“quot;Adam”改為“Kevin”。)
根據(jù)根 XML 元素和一個多值的 customer 屬性定義將要作為根 customers 數(shù)據(jù)對象讀入的 XML 文件。在 XML 文件中 Customers 為每個 customer 元素包含一個數(shù)據(jù)對象。每個 customer 具有兩個屬性:SN 和 firstName 。 |
<customers xmlns="http://customers.com">
<customer SN="1" firstName="Adam" />
<customer SN="2" firstName="Baker" />
</customers>
|
|
讀取文件數(shù)據(jù)。 |
DataObject root = xmlService.load(InputStream);
|
|
遍歷 customer 數(shù)據(jù)對象列表并打印出每個名 (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);
}
|
|
將第一個客戶數(shù)據(jù)對象的 firstName 屬性設(shè)為 Kevin。中間件更新變更摘要(這里并沒有顯示出來)來指示改變了的數(shù)據(jù)。 |
DataObject customer1 = root.getDataObject("customer[1]");
customer1.setString("firstName", "Kevin"); // or
root.setString("customer[1]/firstName", "Kevin");
|
|
將數(shù)據(jù)對象寫入文件。 |
xmlService.save(OutputStream, root);
|
|
結(jié)果是更新的 XML 文檔。 |
<customers xmlns="http://customers.com">
<customer SN="1" firstName="Kevin" />
<customer SN="2" firstName="Baker" />
</customers>
|
|
示例 8. 訪問關(guān)系數(shù)據(jù)庫
雖然復(fù)雜的關(guān)系數(shù)據(jù)庫到 SDO 的映射是可行的,但是這個例子使用的是一個非常簡單的映射:每個數(shù)據(jù)庫表都是一個 SDO 類型,表的每行是 SDO 數(shù)據(jù)對象,而每列是 SDO 屬性。應(yīng)用程序邏輯是相同的:通過執(zhí)行預(yù)先定義好的查詢從數(shù)據(jù)庫中讀取、打印并更新數(shù)據(jù)(將“Adam”改為“Kevin”),將更改保存到數(shù)據(jù)庫。數(shù)據(jù)庫查詢返回 CUSTOMER 表中的兩行:
CUSTOMER ID(整數(shù),主鍵) |
CUSTOMER FIRSTNAME(字符串) |
CUSTOMER LASTNAME(字符串) |
1 |
Adam
|
Smith |
2 |
Baker |
Street |
下面給出了帶有解釋的 SDO 實現(xiàn)。
rdbService 查詢數(shù)據(jù)庫以獲得數(shù)據(jù)。 |
DataObject root = rdbService.get();
|
|
相同的數(shù)據(jù)可能已經(jīng)用 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);
}
|
|
將第一個數(shù)據(jù)對象的 FIRSTNAME 設(shè)為 Kevin。中間件更新變更摘要(這里并沒有顯示出來)來指示改變。 |
DataObject customer1 = root.getDataObject("CUSTOMER[1]");
customer1.setString("FIRSTNAME", "Kevin"); // or
root.setString("CUSTOMER[1]/FIRSTNAME", "Kevin");
|
|
將更新的數(shù)據(jù)寫入數(shù)據(jù)庫。 |
|
現(xiàn)在數(shù)據(jù)庫包含:
CUSTOMER ID(整數(shù),主鍵) |
CUSTOMER FIRSTNAME(字符串) |
CUSTOMER LASTNAME(字符串) |
1 |
Kevin
|
Smith |
2 |
Baker |
Street |
注意,第一行已經(jīng)被更新了。
如果在我們的示例應(yīng)用程序已經(jīng)獲得數(shù)據(jù)圖之后,另外一個應(yīng)用程序訪問數(shù)據(jù)庫并更改了值會怎么樣?在寫入時,數(shù)據(jù)訪問服務(wù)檢查變更摘要來決定如何對數(shù)據(jù)源應(yīng)用更新。數(shù)據(jù)庫可以使用開放式并發(fā)控制 (optimistic concurrency control) 來確保這個改變之前最后包含的值是“Adam”(否則,另外一個應(yīng)用程序可能先改變數(shù)據(jù),可能在該應(yīng)用程序中需要某些錯誤恢復(fù))。某些服務(wù)實現(xiàn)了更為高級的開放式并發(fā)形式;變更歷史記錄提供了那些算法所需要的原始值。
使用 EJB 時,SDO 作為 DTO(也稱作值對象)J2EE 設(shè)計模式。一般來說,訪問實體 EJB(Entity EJB)的每個屬性的開銷非常大,所以傳輸幾個數(shù)據(jù)圖中的 SDO 對象效率更高。會話 EJB(Session EJB)可能有方法產(chǎn)生和使用 SDO 圖來更加高效地直接訪問實體 EJB。Customer
實體 EJB 封裝了對 Customer
記錄的數(shù)據(jù)庫訪問。會話 EJB 提供了訪問方法來從 Customer
實體 EJB 產(chǎn)生和返回 Customer
SDO 圖。
示例 9. 返回 SDO 和由 SDO 更新實體 EJB 的會話 Bean 接口
public interface CustomerSession {
Customer getCustomerByID(String customerID);
Customers getCustomersByLastName(String lastName);
void updateCustomers(Customers customers);
}
|
Customers
是包含若干客戶的 SDO 根數(shù)據(jù)對象。Customers
的 List
包含 Customer
數(shù)據(jù)對象。ChangeSummary
用來記錄對 Customer
對象所做的全部更改,包括任何的添加、刪除或更新。updateCustomers()
方法利用所做的更改來更新 Customer
實體 EJB,并且可以在一個事務(wù)中批處理對數(shù)據(jù)源的更改。
示例 10. 使用 Java 的 Customer 的 SDO 類型定義
public interface Customers {
List<Customer> getCustomers();
ChangeSummary getChanges();
}
|
總結(jié)
SDO 為所有數(shù)據(jù)源啟用了對應(yīng)用程序數(shù)據(jù)的統(tǒng)一訪問和公共編程模型,而不管數(shù)據(jù)存儲在何地以及如何存儲。SDO 利用了 XML 的簡易性,而又沒有引入 XML Schema 的復(fù)雜性或序列化的性能問題。通過同時使用 SDO 和 SOA,可以將系統(tǒng)編程任務(wù)從業(yè)務(wù)邏輯中分離出來,并且將其封裝在可重用的服務(wù)之中,而不是所有編程人員都必須掌握這些技能才能入門。它們在沒有陷入技術(shù)和實現(xiàn)細(xì)節(jié)的情況下簡化了業(yè)務(wù)應(yīng)用程序的編程,防止了技術(shù)改變產(chǎn)生的影響。有了 SDO,業(yè)務(wù)應(yīng)用程序就是名副其實的業(yè)務(wù)應(yīng)用程序。