程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 數據庫知識 >> DB2數據庫 >> DB2教程 >> DB2 XML 編程,第 2 部分: 在應用程序體系結構中使用 XML 數據庫支持

DB2 XML 編程,第 2 部分: 在應用程序體系結構中使用 XML 數據庫支持

編輯:DB2教程

簡介

XML 在數據庫中的地位在過去兩年中已經發生了變化,從 “臨時工” 變成了重要成員。它不再需要改變本身來適應關系環境。它可以保持其層次化性質,同時利用關系數據庫環境的功能和穩定性。實際上,一些關系性元素已經采用某些技術讓它們看起來像 XML,以便利用層次化 XML 模型豐富的功能。

本文討論新的 XML 存儲和查詢環境如何處理本系列 第 1 部分 中的 XML 數據模型。還要說明,在采用新的基於 XML 的應用程序開發體系結構之後,數據庫模式會變得更簡單更自然。還演示如何按照在應用程序中查詢數據的相同方式查詢數據庫中的 XML 數據。最後,討論如何結合關系數據和 XML 數據,從而同時獲得這兩個環境的優勢。

XML 數據庫基礎

盡管大多數主流關系數據庫都有某種 XML 支持,但是 DB2 的 pureXML™ 支持要健壯和高效得多,這使它成為試驗 XML 編程模型的理想數據庫。本文主要關注如何在應用程序體系結構中使用新的 XML 數據庫支持。

DB2 允許存儲、查詢、操作和發布:

關系數據 — SQL

采用 XML 形式的關系數據 — SQL/XML

XML 數據 — XQuery

混合型數據(關系數據和 XML 數據) — SQL/XML 和 XQuery


圖 1. DB2 混合型存儲
DB2 XML 編程,第 2 部分: 在應用程序體系結構中使用 XML 數據庫支持

在數據庫中存儲 XML

關系數據庫中的 XML 支持的主要好處是,可以在同一個表中同時存儲關系數據和 XML 數據。另外,盡管 XML 在內部存儲為層次化(樹)格式,但是它看起來像是存儲在數據庫表的單一列中(就像 CLOB 或 BLOB)。

從第 1 部分中的數據對象可以看出,有兩個表,每個表至少有兩列。


清單 1. 表
CREATE TABLE CUSTOMER_TABLE ( 
CUSTOMERID CHARACTER (12) NOT NULL, 
CUSTXML XML NOT NULL , 
CONSTRAINT CC1183665042494 PRIMARY KEY ( CUSTOMERID) ) 
 
CREATE TABLE PURCHASE_TABLE ( 
CUSTOMERID CHARACTER (12) NOT NULL , 
ITEMXML XML NOT NULL , 
CONSTRAINT CC1183665244645 FOREIGN KEY 
(CUSTOMERID) REFERENCES CUSTOMER_TABLE (CUSTOMERID) 
ON DELETE CASCADE ON UPDATE NO ACTION 
ENFORCED ENABLE QUERY OPTIMIZATION ) 

顯然,通過將應用程序的數據對象存儲為 XML,關系模式大大簡化了。另外,基礎結構仍然是關系型的,這使 XML 數據能夠利用關系數據庫的實用功能,比如觸發器、約束和外鍵關系。

因為從邏輯上看 XML 列與 VARCHAR、CLOB 或 BLOB 列相似,所以 INSERT 語句也是相似的。

insert into CUSTOMER_TABLE values('hardeep', 
'<Customer customerid="hardeep" firstname="hardeep" lastname="singh"/>') 

在 Java™ 程序中執行插入的代碼也是相似的:


清單 2. 在 Java 程序中執行插入
String insertsql= "insert into PURCHASE_TABLE values(?,?)"; 
PreparedStatement iStmt=connection.prepareStatement(insertsql); 
File inputfile= new File(filename); //filename is the path of the XML file 
long filesize=inputfile.length(); 
BufferedReader in = new BufferedReader(new FileReader(inputfile)); 
iStmt.setCharacterStream(1,in,(int)filesize); 
int rc= iStmt.executeUpdate(); 

為了更好地了解混合型存儲,我們來看看 XML 數據的邏輯視圖,體會 XML 數據如何看起來像是存儲在關系數據庫表中。

注意:盡管不同關系數據庫廠商的 XML 物理存儲技術可能不一樣,但是邏輯視圖是相似的。


圖 2. DB2 混合型存儲邏輯視圖
DB2 XML 編程,第 2 部分: 在應用程序體系結構中使用 XML 數據庫支持

圖片看不清楚?請點擊這裡查看原圖(大圖)。

查詢 XML

在展開數據庫模式模型時,可以看到關系表和列。如果展開 XML 列,模式會從關系模型變成 XML 層次化模型。現在,您應該意識到其實有兩個模式(一個關系模式和一個 XML 模式),但是把它們當作一個整體;理解了這一點,就能夠以更自然的方式在這個統一的模式中進行導航和查詢。

對於清單 1 所示的統一模式,如果希望獲得 CUSTOMER_TABLE 中 CUSTXML 列的數據,那麼可以在查詢中指定 CUSTXML 列的路徑作為目標。

SELECT CUSTXML FROM CUSTOMER_TABLE where customerid='hardeep'; 

這會返回 hardeep 的 CUSTXML 列中的客戶數據。

現在,考慮如何獲得其 lastname 為 singh 的客戶數據。在這種情況下,需要指定每個 XML 文檔的 lastname 屬性的路徑(CUSTOMER_TABLE.CUSTXML/Customer/@lastname)並檢查它是否是 singh。

在完美的環境中,查詢應該是 Select * from CUSTOMER_TABLE where CUSTXML/Customer/@lastname='singh'。但是在真實環境中,需要用數據庫查詢引擎能夠理解的一種語法編寫查詢。數據庫領域已經引入了一種稱為 XQuery 的新語言,可以用它查詢 XML 文檔。SQL 添加了可以理解這種語言的新函數,從而將關系和 XML 環境聯系起來了。所以搜索姓氏為 singh 的客戶的查詢如下:

select CUSTXML from CUSTOMER_TABLE 
where xmlexists ('$cust/Customer[@lastname= "singh" ]' passing CUSTXML AS "cust" ) 

還可以在 Java 程序中通過參數化查詢執行這個調用:

select CUSTXML from CUSTOMER_TABLE 
where XMLexists ('$cust/Customer[@lastname= $lname ]' 
passing CUSTXML AS "cust" , cast(? as VARCHAR(12)) as "lname") 

掌握了向 SQL/XML 函數傳遞參數的語法之後,您會發現,在針對關系數據和 XML 數據的基本混合型查詢中,XML 查詢通常包含 XPath 表達式。這與在應用程序層中操作 XML 數據模型的方法非常相似(見第 1 部分),在那裡許多代碼通過對 Document Object Model(DOM)包裝器進行 XPath 調用來查詢和操作 XML 數據。

注意:在 Viper 2 中,對傳遞給一些 SQL/XML 函數的參數做了簡化。例如,在前面的查詢中,XMLExists 的 passing 子句不需要指定 CUSTXML 列。

select CUSTXML from CUSTOMER_TABLE 
where xmlexists ('$CUSTXML/Customer[@lastname= $lname ]' 
passing cast(? as VARCHAR(12)) as "lname") 

將應用程序邏輯放在數據庫中

XQuery 提供了大多數高級語言的所有基本功能(if-then-else、for、變量、函數和算術操作符)。因此,可以將業務邏輯嵌入查詢中。另外,它還提供許多常用的 XSLT 映射,所以它不但能夠執行查詢,還能夠在數據庫中轉換 XML 輸出。

我們仍然以第 1 部分中 Customer 示例的 XML 數據模型為例。

<Customer customerid ="" firstname="" lastname="" > 
<Items><Item ID="" description="" purchaseDate="" price="" /></Items> 
</Customer> 

用 DB2 查詢替代應用程序代碼

我們不必在應用程序層中合並來自兩個表的 XML 數據,可以在數據庫中用一個 SQL/XML 查詢實現相同的效果。這需要一個 CUSTOMER_TABLE.CUSTXML/Customer/@customerid 到 PURCHASE_TABLE.ITEMXML/Customer/@customerid 的一對多聯結。


圖 3. 聯結兩個 XML 列
DB2 XML 編程,第 2 部分: 在應用程序體系結構中使用 XML 數據庫支持

圖片看不清楚?請點擊這裡查看原圖(大圖)。


清單 3. 查詢兩個 XML 列

values(XMLquery(' 
 
for $Customer in db2-fn:xmlcolumn( "CUSTOMER_TABLE.CUSTXML")/Customer 
 
where $Customer/@customerid= $customerid 
 
return 
<Customer customerid ="{$Customer/@customerid}" 
firstname ="{$Customer/@firstname}" lastname ="{$Customer/@lastname}" >{ 
 
for $Customer0 in db2-fn:xmlcolumn("PURCHASE_TABLE.ITEMXML")/Customer 
where $Customer0/@customerid= $Customer/@customerid 
return $Customer0/Item 
 
}</Customer> 
 
' passing cast( ? AS varchar(255) ) as "customerid" )) 

對於客戶 hardeep 購買的所有商品,產生的 XML 如下:


圖 4. 查詢結果
DB2 XML 編程,第 2 部分: 在應用程序體系結構中使用 XML 數據庫支持

在上面的查詢中,必須構造外層的 Customer 元素並添加來自 CUSTXML 列數據的屬性。DB2 Viper 2(beta)支持 XQuery 更新表達式,可以修改 XML 文檔,所以不需要構造外層的 Customer 元素。可以使用來自客戶表的一個 XML 文檔,並將來自購買表的商品數據作為子元素插入其中。


清單 4. 針對兩個 XML 列的 Viper 2 查詢
values(XMLquery(' 
   
for $Customer in db2-fn:xmlcolumn( "CUSTOMER_TABLE.CUSTXML")/Customer 
 
let $items:=(<Items>{ 
 
for $Customer0 in db2-fn:xmlcolumn("PURCHASE_TABLE.ITEMXML")/Customer 
where $Customer0/@customerid= $Customer/@customerid 
return $Customer0/Item 
 
}</Items>) 
 
where $Customer/@customerid= $customerid 
 
return 
transform 
copy $cust:=$Customer 
modify( 
do insert $items as last into $cust) 
return $cust 
 
' passing hardeep as "customerid" )) 

在上面的查詢中,不但搜索、獲取和合並了數據庫中存儲的 XML 文檔部分,還在產生的 XML 中添加了新元素。另外,hardeep 被隱式轉換為 XML 類型(xs:string)。

對比數據庫查詢和 Java 應用程序代碼

如果將上面的查詢與第 1 部分中的 Java 代碼(清單 6. 重寫應用程序來使用 XML 模型)做一下對比,就會發現它們的邏輯非常相似。

從 CUSTOMER_TABLE 中選擇 Customer 信息。

構造一個 Items 元素並在 PURCHASE_TABLE 中搜索這位客戶購買的所有商品。

循環遍歷列表中的每個商品並將它們插入 Items 元素。

將 Items 元素插入 Customer 元素。

創建存儲過程

為了將數據庫中的業務邏輯與應用程序代碼隔離開,一種好方法是為這個查詢創建一個存儲過程。


清單 5. 創建存儲過程
CREATE PROCEDURE customerItems(IN custid varchar(12)) 
DYNAMIC RESULT SETS 1 
LANGUAGE SQL 
BEGIN 
DECLARE c_cur CURSOR WITH RETURN FOR 
 
values(XMLquery(' 
 
for $Customer in db2-fn:xmlcolumn( "CUSTOMER_TABLE.CUSTXML")/Customer 
 
let $items:=(<Items>{ 
 
for $Customer0 in db2-fn:xmlcolumn("PURCHASE_TABLE.ITEMXML")/Customer 
where $Customer0/@customerid= $Customer/@customerid 
return $Customer0/Item 
 
}</Items>) 
 
where $Customer/@customerid= $customerid 
 
return 
transform 
copy $cust:=$Customer 
modify( 
do insert $items as last into $cust) 
return $cust 
 
' passing custid as "customerid" )) 
OPEN c_cur; 
END 

用存儲過程調用替代應用程序代碼

應用程序代碼現在可以對 DB2 進行存儲過程調用並將 XML 傳遞給 DOM 包裝器。第 1 部分中的 XML 數據模型的應用程序代碼(清單 6. 重寫應用程序來使用 XML 模型,2-8 行)可以簡化為:

2. ResultSet dbResult = dbstmt.executeQuery("call customerItems ("+custid+")" 
3. XMLParse customerXML = new XMLParse(dbResult. getString(1)); 

一個更精細的示例

現在考慮一個比較精細的場景,這個場景還要計算每個商品的保險費。為了讓這個場景有點兒難度,保險費不但每天變動,而且隨價格變化。這意味著,不但要向查詢傳遞 customerid,還要傳遞保險費率。現在,假設您每天在保險公司提供的一個 Web 服務中查詢最新的保險費率。保險費率信息采用 XML 文檔的形式。

<insurance> 
<rate price="100" currency="$" rate=".02"/> 
<rate price="500" currency="$" rate=".018"/> 
<rate price="" currency="$" rate=".015"/> 
</insurance> 

可以修改前面的存儲過程來計算保險費。


清單 6. 計算每個商品的保險費的存儲過程
CREATE PROCEDURE customerItemsWithInsurance(IN custid varchar(12), rate XML) 
DYNAMIC RESULT SETS 1 
LANGUAGE SQL 
BEGIN 
DECLARE c_cur CURSOR WITH RETURN FOR 
 
values(XMLquery(' 
 
for $Customer in db2-fn:xmlcolumn( "CUSTOMER_TABLE.CUSTXML")/Customer 
 
let $items:=( 
<Items>{ 
for $Customer0 in db2-fn:xmlcolumn("PURCHASE_TABLE.ITEMXML")/Customer 
 
let $insurance:=<insurance currency="{($rate//rate[@price=""]/@currency)}"> 
{( 
if($Customer0/Item/@price > 500) then ( 
$Customer0/Item/@price * $rate//rate[@price=""]/@rate 
) 
else ( 
 
if($Customer0/Item/@price > 100) then ( 
$Customer0/Item/@price * $rate//rate[@price="500"]/@rate 
) 
else ( 
$Customer0/Item/@price * $rate//rate[@price="100"]/@rate 
) 
 
) 
)}</insurance> 
 
where $Customer0/@customerid= $Customer/@customerid 
 
return 
transform 
copy $item:=$Customer0/Item 
modify( do insert $insurance as last into $item) 
return $item 
}</Items> 
) 
 
where $Customer/@customerid= $customerid 
 
return 
transform 
copy $cust:=$Customer 
modify(do insert $items as last into $cust) 
return $cust 
 
' passing custid as "customerid", rate as "rate" )); 
 
OPEN c_cur; 
END 

對這個存儲過程的調用接受兩個運行時參數,customerid 和保險 XML。

call customerItemsWithInsurance(?,?)

顯然,在上面的示例中,如果要操作的數據庫數據是 XML 格式的,那麼可以使用 XQuery 實現比只使用 SQL 時更多的業務邏輯。另外,查詢中使用的 XML 甚至不需要在數據庫中存在。因此,SQL/XML 查詢中涉及的 XML 數據可以按照層次化形式存儲在數據庫中,可以由 SQL/XML 函數生成,甚至可以作為運行時參數傳遞給查詢。數據庫和應用服務器之間的差異逐漸變得模糊了。

優點和缺點

所有新技術在剛剛出現時都會有一些問題。一些問題是由於實現還不成熟造成的,其他問題是由於開發人員還不適應變化。

盡管性能有所改進,但是仍然比不上關系數據。

XQuery 是一種新語言,而且一些 SQL/XML 函數的語法需要花時間適應。

有許多遺留數據采用關系格式。

最重要的是,這是一種創建業務應用程序和數據模式的新方式,它與當前的面向對象應用程序和規范化關系模式很不一樣。

能夠對這類查詢進行調試和優化的工具還不多。

盡管存在這些缺點,但是新的模型管理數據的方式更加自然。在應用程序層和數據庫層中都按原樣維護和操作業務數據信息,而且您將第 3 部分中看到,甚至在客戶機層中也是如此。

盡管外圍語言可能不一樣(Java、XQuery、JavaScript、PHP),但是在所有層中用來在 XML 文檔中移動的語言都是相同的(XPath)。

即使遺留數據是關系型的,但是通過使用 Viper 2 中引入的一些 SQL/XML 函數,可以輕松地查詢這些數據並將它們轉換為 XML。看一下第 1 部分中的示例 “第二種情況 —— 所有數據在數據庫中存儲為關系形式”。可以使用 Viper 2 中引入的 XMLROW 函數簡化這個查詢。

Select XMLROW (customerid, firstname, lastname OPTION as attributes ROW Customer) 
from customer_table where customerid=? 

還可以在關系數據和 XML 數據之間創建聯結。在這個示例場景中,如果有第三個表,其中包含購買商品的產品說明,而且這是一個關系表,那麼可以使用商品 ID 執行聯結,從而獲得購買的每種商品的產品說明。



圖 5. 聯結關系列和 XML 列
DB2 XML 編程,第 2 部分: 在應用程序體系結構中使用 XML 數據庫支持

Select details, weight from SQLPRODUCT, ITEM_TABLE 
where xmlexists ('$itemXML/item[@itemid=$pid]' 
passing ITEM_TABLE.ITEMXML AS "itemXML", SQLPRODUCT.PID AS "pid" ) 

在 DB2 9 中,可以使用 passing 子句向 SQL 語句中嵌入的 XQuery 傳遞運行時參數,但是不能向 XQuery 中嵌入的 SQL 傳遞參數。在 Viper 2 中,這個限制已經消除了,現在可以向 XQuery 中嵌入的關系查詢傳遞運行時變量。



清單 7. 向 XQuery 中嵌入的 SQL 傳遞運行時變量

values(XMLquery(' 
 
for $Customer0 in db2-fn:xmlcolumn("PURCHASE_TABLE.ITEMXML")/Customer 
where $Customer0/@customerid= $custid 
return ( 
$Customer0/Item, 
db2-fn:sqlquery( 
''select XMLrow(details, description, weight option ROW "description") 
from sqlproduct where pid= parameter(1)'', $Customer0/Item/@ID)) 
 
' passing cast( ? AS varchar(255) ) as "custid" )) 

因此,即使一部分數據位於關系表中,一些數據是 XML,現在都可以在 SQL 查詢、XQuery 或這兩者中在 XML 數據和關系數據之間進行動態聯結。

在某些情況下,性能可能不是大問題,因為:

能夠為數據庫中存儲的 XML 文檔創建基於 XPath 表達式的索引。

create index custfname on customer_table(info) generate key 
using XMLpattern '/Customer/@firstname' as sql varchar(64) 

因為數據庫模式更簡單,所以減少了所需的聯結數量。

因為現在可以在查詢中縮減數據,然後再把數據發送給應用程序,所以可以減少 I/O。

隨時可以使用 SQL/XML 函數(比如 XMLTable)將 XML 文檔中的關鍵信息提取到關系列中,並為它們創建關系索引。

可以為 XML 文檔創建文本搜索索引。

結束語

XML 已經取得了穩固的地位。大多數行業和政府組織都對他們的 XML 模式做了標准化,並要求電子文檔必須符合這些模式。既然通過線路交換的 B2B 數據已經采用了 XML 格式,為什麼不在數據庫中按原樣(pureXML)存儲這些數據呢?將數據存儲為 XML 之後,可以使用 XQuery 和標准的 SQL/XML 對它進行編制索引、查詢、檢驗、操作、轉換和更新。隨著越來越多的應用程序邏輯被放在查詢中,數據庫可以以 Web 服務和 feed 的形式公開它的存儲過程,從而成為面向服務體系結構(SOA)環境中活躍的參與者。

“舊的秩序已經改變了,新的秩序正在形成。”(The old order changeth, yIElding place to new.) Morte d'Arthur

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved