引言:什么是 SDO?
Service Data Object (SDO) 2.0 是一個開放標準數據模型編程 API,允許開發人員在較高的級別方便地操作數據。盡管 SDO 1.0 以相同的數據抽象為目標,但有幾個大的缺點,其中主要的一點是缺少 Helper 類,如 XSDHelper
, XMLHelper
等。而最終結果是,開發人員被迫使用 SDO 1.0 實現 API(來自 Eclipse Modeling Framework (EMF) SDK)。
當前的實現 (SDO 2.0) 使用 EMF 2.2 SDK,但這個 SDO 2.0 實現細節并不會影響開發人員根據新 API 編寫程序。將來,開放源代碼社區(通過 Apache Software Foundation)可能會決定提供不同的 SDO 2.0 實現,但這不應影響基于 SDO 2.0 API 構建的應用程序。
了解這個新 API 的優勢的最基本方法是使用 SDO 2.0 來創建符合 XML 模式 (XSD) 的 XML 文檔并對其進行讀取操作。要在不使用 SDO 2.0 的情況下完成相同的工作,開發人員需要理解 XML 解析器如何工作,并將數據解析邏輯與應用程序緊密集成。如果以后 XSD 需要更改,將需要對應用程序的各處進行調整,從而可能對代碼的質量帶來災難性的影響。
SDO 2.0 API 的新用戶很難直接通過學習規范來理解相關概念。為了提供一定的幫助,我們從 XML Schema Primer(請參閱參考資料)創建了一個示例,以說明如何使用 SDO 2.0 API 進行以下任務:
場景:購買訂單信息收集和配送
以下是我們將嘗試通過我們的示例實現的使用場景。首先,公司確定購買訂單(purchase order,PO)信息的要求。完成此任務后,我們就開始創建靜態表單,以設計信息的結構。在數據庫中,此靜態表單即表模式。在 XML,此靜態表單即 XML 模式 (XSD)。如果以后需要收集更多的客戶信息,我們將需要重新設計表單/表模式/XSD。
下訂單時,客戶需要填寫 PO 表單,或向操作員提供相同的信息,以便填寫此表單。表單中的信息將隨后由公司用于對訂單進行配送。
公司接收到 PO 信息后,很有可能會將其進行保存,并在以后對其進行讀取以用于各種目的(如提供客戶支持)。在此示例中,我們可以將信息以 XML 格式保存,然后使用 SDO API 進行讀取。
圖 1
顯示了從客戶 Robert Smith 收集的 PO 信息示例。將創建 XSD 文件,以采用結構化的方式存儲此示例購買訂單。將用一個名為 CreatePurchaseOrder.java
的示例程序來采用 XML 格式創建示例 PO(名為 po.xml
)。最后,將使用 ReadPurchaseOrder.java
來演示如何從 po.xml
讀取訂單信息。
圖 1. 來自 Rober Smith 的示例購買訂單信息
Order date: 1999-10-20
Shipping information: Name:Alice Smith Street:123 Maple Street City:Mill Valley State:CA Zip code: 90952 Country:US
Billing information: Name:Robert Smith Street:8 Oak Avenue City:Mill Valley State:PA Zip code: 95819 Country:US
Order Items: 1. Part number:872-AA Product name:Lawnmower Quantity: 1 Price: 148.95 Comment:Confirm this is electric
2. Part number:926-AA Product name:Baby Monitor Quantity: 1 Price: 39.98 Ship date: 1999-05-21
Comment: Hurry, my lawn is going wild!
|
購買訂單的 XML 模式
基于圖 1 所示的示例購買訂單,可以使用一個 PO 類型來表示訂單。我們將其命名為 PurchaseOrderType
。PurchaseOrderType
的實例可以包含四個主要數據,如下所示:
-
shipping information
-
billing information
-
order items information
-
comment
shipping information 和 billing information 可包含更多的數據,如 name
、street
、city
、state
、zip
和 country
。Order items information 可以包含客戶所購買的物品的很多信息。每個物品可能包含 part number
、product name
、quantity
, price
、ship date
和 comment
。PO 的 comment 區域包含一個字符串值,并不要求采用更結構化的信息。
清單 1 顯示了結構化為 XSD 格式的文件中包含的所有購買訂單信息。
清單 1. PO.xsd
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:po="http://www.example.com/PO" targetNamespace="http://www.example.com/PO">
<xsd:element name="purchaseOrder" type="PurchaseOrderType"/>
<xsd:element name="comment" type="xsd:string"/>
<xsd:complexType name="PurchaseOrderType">
<xsd:sequence>
<xsd:element name="shipTo" type="USAddress"/>
<xsd:element name="billTo" type="USAddress"/>
<xsd:element ref="comment" minOccurs="0"/>
<xsd:element name="items" type="Items"/>
</xsd:sequence>
<xsd:attribute name="orderDate" type="xsd:date"/>
</xsd:complexType>
<xsd:complexType name="USAddress">
<xsd:sequence>
<xsd:element name="name" type="xsd:string"/>
<xsd:element name="street" type="xsd:string"/>
<xsd:element name="city" type="xsd:string"/>
<xsd:element name="state" type="xsd:string"/>
<xsd:element name="zip" type="xsd:decimal"/>
</xsd:sequence>
<xsd:attribute name="country" type="xsd:NMTOKEN" fixed="US"/>
</xsd:complexType>
<xsd:complexType name="Items">
<xsd:sequence>
<xsd:element name="item" minOccurs="0" maxOccurs="unbounded">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="productName" type="xsd:string"/>
<xsd:element name="quantity">
<xsd:simpleType>
<xsd:restriction base="xsd:positiveInteger">
<xsd:maxExclusive value="100"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:element>
<xsd:element name="USPrice" type="xsd:decimal"/>
<xsd:element ref="comment" minOccurs="0"/>
<xsd:element name="shipDate" type="xsd:date" minOccurs="0"/>
</xsd:sequence>
<xsd:attribute name="partNum" type="po:SKU" use="required"/>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
<xsd:simpleType name="SKU">
<xsd:restriction base="xsd:string">
<xsd:pattern value="\d{3}-[A-Z]{2}"/>
</xsd:restriction>
</xsd:simpleType>
|
還可以使用統一建模語言(Unified Modeling Language,UML)類關系圖來對購買訂單信息進行結構化,如圖 2 中所示。
圖 2. 購買訂單的 UML 類關系圖
XSD po.xsd
文件和該 UML 類關系圖具有以下區別:
- 類關系圖定義了
ItemType
,而并未在 po.xsd.diagram
中定義此項。盡管 po.xsd
并不具體定義 ItemType
,XSD 規范仍可將 ItemType
作為匿名類型處理。
- XSD 中所有
xsd:<type>
在類關系圖中都縮寫為了 <type>
。在 XSD 中,xsd:<type>
模式中的 xsd 用于指示類型所屬的命名空間。在類關系圖中,為了簡單起見而將其省略了。
-
SKU type
未在類關系圖中定義。SKU type
事實上是在 po
命名空間中定義的規范化字符串類型。在類關系圖中,它由 string type
加以表示。
創建 XML 格式的示例 PO
清單 2 中所示的示例 CreatePurchaseOrder.java
程序用于創建一個 PO,該 PO 采用名為 po.xml
的 XML 格式(如清單 3 所示)。
清單 2. CreatePurchaseOrder.java
/**
* Author: Fuhwei Lwo
*/
import java.io.FileOutputStream;
import java.io.FileInputStream;
import java.io.OutputStream;
import commonj.sdo.DataObject;
import commonj.sdo.helper.DataFactory;
import commonj.sdo.helper.XMLHelper;
import commonj.sdo.helper.XSDHelper;
public class CreatePurchaseOrder {
private static final String PO_MODEL = "po.xsd";
private static final String PO_NAMESPACE = "http://www.example.com/PO";
private static final String PO_XML = "po.xml";
private static void definePOTypes() throws Exception {
FileInputStream fis = new FileInputStream(PO_MODEL);
XSDHelper.INSTANCE.define(fis, null);
fis.close();
}
public static void main(String[] args) throws Exception {
definePOTypes();
DataObject purchaseOrder =
DataFactory.INSTANCE.create(PO_NAMESPACE, "PurchaseOrderType");
purchaseOrder.setString("orderDate", "1999-10-20");
DataObject shipTo = purchaseOrder.createDataObject("shipTo");
shipTo.set("country", "US");
shipTo.set("name", "Alice Smith");
shipTo.set("street", "123 Maple Street");
shipTo.set("city", "Mill Valley");
shipTo.set("state", "CA");
shipTo.setString("zip", "90952");
DataObject billTo = purchaseOrder.createDataObject("billTo");
billTo.set("country", "US");
billTo.set("name", "Robert Smith");
billTo.set("street", "8 Oak Avenue");
billTo.set("city", "Mill Valley");
billTo.set("state", "PA");
billTo.setString("zip", "95819");
purchaseOrder.set("comment", "Hurry, my lawn is going wild!");
DataObject items = purchaseOrder.createDataObject("items");
DataObject item1 = items.createDataObject("item");
item1.set("partNum", "872-AA");
item1.set("productName", "Lawnmower");
item1.setInt("quantity", 1);
item1.setString("USPrice", "148.95");
item1.set("comment", "Confirm this is electric");
DataObject item2 = items.createDataObject("item");
item2.set("partNum", "926-AA");
item2.set("productName", "Baby Monitor");
iteim2.setInt("quantity", 1);
item2.setString("USPrice", "39.98");
item2.setString("shipDate", "1999-05-21");
OutputStream stream = new FileOutputStream(PO_XML);
XMLHelper.INSTANCE.save(purchaseOrder, PO_NAMESPACE, "purchaseOrder", stream);
}
}
|
清單 3. Po.xml
<?xml version="1.0" encoding="ASCII"?>
<po:purchaseOrder xmlns:po="http://www.example.com/PO" orderDate="1999-10-20">
<shipTo country="US">
<name>Alice Smith</name>
<street>123 Maple Street</street>
<city>Mill Valley</city>
<state>CA</state>
<zip>90952</zip>
</shipTo>
<billTo country="US">
<name>Robert Smith</name>
<street>8 Oak Avenue</street>
<city>Mill Valley</city>
<state>PA</state>
<zip>95819</zip>
</billTo>
<comment>Hurry, my lawn is going wild!</comment>
<items>
<item partNum="872-AA">
<productName>Lawnmower</productName>
<quantity>1</quantity>
<USPrice>148.95</USPrice>
<comment>Confirm this is electric</comment>
</item>
<item partNum="926-AA">
<productName>Baby Monitor</productName>
<quantity>1</quantity>
<USPrice>39.98</USPrice>
<shipDate>1999-05-21</shipDate>
</item>
</items>
</po:purchaseOrder>
|
該示例 Java 程序首先通過調用 XSDHelper.INSTANCE.define()
方法向 SDO 運行時注冊 po.xsd
中描述的所有類型。然后,它從 PurchaseOrderType
創建一個根 DataObject
。該程序將從該處使用 DataObject
API 來構建 DataObject
樹,以表示購買訂單信息,如圖 3 中所示。
圖 3. DataObject 樹
在圖 3 中,每個矩形都劃分為兩個隔間。頂部間隔(灰色)指示 DataObject
實例名稱及其實際類型;底部間隔指示所包含的屬性。例如,樹的根元素為 purchaseOrder DataObject
實例;其實際類型為 PurchaseOrderType
,在清單 1 中的 PO.xsd
中定義。在此 DataObject
實例中,包含兩個屬性值——orderDate
和 comment
。
創建 DataObject
樹后,該程序調用 XMLHelper.INSTANCE.save()
方法來將從 purchaseOrder DataObject
實例開始的樹內容保存到 XML 文檔中(此例中為 po.xml
)。事實上,可以將樹中任何 DataObject
實例指定為 XMLHelper.INSTANCE.save()
方法的第一個參數,save()
方法將保存從所指定的實例開始的所有 DataObject
實例。
讀取示例 PO
CreatePurchaseOrder.java
創建了 po.xml
后,我們可以編譯并運行清單 4 中所示的 ReadPurchaseOrder.java
,以演示如何使用 SDO API 來遍歷 po.xml
的內容。該應用程序將執行以下操作:
- 進行檢查,以確保已向 SDO 運行時注冊了
po.xsd
中定義的類型
- 調用
XMLHelper.load()
方法來將 po.xml
加載到內存中(使用 XMLDocument
實例 xmlDoc
加以表示)
- 調用
xmlDoc.getRootObject()
方法來檢索名為 purchaseOrder
的 DataObject
樹的根對象,該對象與圖 3 中所示的 DataObject
樹關系圖中的 purchaseOrder DataObject
對應
-
purchaseOrder DataObject
返回后,遍歷 DataObject
樹,以檢索關于此 PO 的所有信息
清單 4. ReadPurchaseOrder.java
/**
/**
* Author: Fuhwei Lwo
*/
import java.io.FileInputStream;
import java.util.List;
import commonj.sdo.DataObject;
import commonj.sdo.helper.XMLDocument;
import commonj.sdo.helper.XMLHelper;
import commonj.sdo.helper.XSDHelper;
public class ReadPurchaseOrder {
private static final String PO_MODEL = "po.xsd";
private static final String PO_XML = "po.xml";
private static void definePOTypes() throws Exception {
FileInputStream fis = new FileInputStream(PO_MODEL);
XSDHelper.INSTANCE.define(fis, null);
fis.close();
}
public static void main(String[] args) throws Exception {
definePOTypes();
FileInputStream fis = new FileInputStream(PO_XML);
XMLDocument xmlDoc = XMLHelper.INSTANCE.load(fis);
DataObject purchaseOrder = xmlDoc.getRootObject();
System.out.println("Order date: " + purchaseOrder.get("orderDate"));
System.out.println("Comment: " + purchaseOrder.get("comment"));
DataObject shipTo = purchaseOrder.getDataObject("shipTo");
System.out.println("Ship to name: " + shipTo.get("name"));
DataObject billTo = purchaseOrder.getDataObject("billTo");
System.out.println("Bill to name: " + billTo.get("name"));
System.out.println();
DataObject items = purchaseOrder.getDataObject("items");
List itemList = items.getList("item");
for (int i=0; i<itemList.size(); i++) {
DataObject item = (DataObject)itemList.get(i);
System.out.println("Part num: " + item.get("partNum"));S
System.out.println("Product name: " + item.get("productName"));
} // for
}
}
|
清單 4 中以黑體顯示的 System.out.println()
顯示各種數據對象的屬性值,包括:
-
purchaseOrder DataObject
的 orderDate
和 comment
屬性
-
shipTo DataObject
的 name
屬性
-
billTo DataObject
的 name
屬性
- 各種數據對象的
partNum
和 productName
屬性
圖 4. 運行 ReadPurchaseOrder 的控制臺輸出
Order date: 1999-10-20
Comment:Hurry, my lawn is going wild! Ship to name:Alice Smith Bill to name:Robert Smith
Part num:872-AA Product name:Lawnmower Part num:926-AA Product name:Baby monitor |
在圖 4 中,我們可以看到運行 ReadPurchaseOrder
Java 代碼的輸出。
結束語:SDO 將成為事實上的數據模型編程 API
SDO 2.0 API 提供了創建和訪問數據的一致方式,使開發人員不必了解分析和維護數據完整性的底層實現細節。SDO 2.0 目前是 Apache Software Foundation 下的一個試驗性子項目(稱為 Tuscany),預期將成為 SOA 開發的數據模型編程 API 的事實標準。請參閱參考資料部分,以獲得一個相關鏈接。
可以從上面的示例中看出,SDO 2.0 API 使您完全不必知道和使用 XML 解析器 API 來讀取、寫入和操作數據。如果使用 Java 創建了 DataObject
來表示符合您所定義的 XML 模式的 XML 數據,SDO 2.0 將提供足夠的方便性和靈活性,讓您將精力放在如何使用數據上。因此,它可為您帶來極大的好處,從而提高開發工作效率和產品質量。
總之,通過使用 SDO 2.0 提供的數據抽象技術,您可以根據所定義的業務邏輯來處理數據,以滿足您的業務需求。這可以幫助您簡化業務應用程序開發,也能提高團隊的工作效率和工作質量。
參考資料
學習
討論
關于作者
|
|
|
Fuhwei Lwo 目前是 WebSphere Service Data Objects (SDO) 的團隊負責人。他主要的工作重點是推動在面向服務的體系結構(Service Oriented Architecture,SOA)中將 SDO 作為數據數據模型編程 API 成功采用。
|