[原創]SQLXML系列之一:SQLXML初體驗——用XML代替SQL來操作數據庫

隨着Internet的飛速發展,W3C成員意識到必須找到一種辦法將數據和Web的表現方式分離出來,於是XML誕生了。當今的XML已經成爲IT領域各個數據(特別是文檔)的首選格式。由於它具有標記不同字段的能力,因此使搜索變得更簡單。從微軟發佈SQL Server 2000的時候,就讀XML數據的存儲和檢索提供內置的支持。而且微軟早已意識到必須對其不斷地改進,以便和不斷髮展的W3C的XML標準保持一致。在微軟發佈SQL Server 2000的幾個月之後,它便在Web站點上發佈了完全可以支持XML特性的軟件包提供免費的下載。這些軟件包被稱作SQLXML(XML for SQL Server),當時的版本是3.0。5年之後,SQL Server 升級到了SQL Server 2005,提供了一系列的新的功能和特性,比如對新的XML數據類型的支持;提供了新的Data Access Provider——Native Client;等等。爲了提供對這個新的功能的支持,和對原來版本的改進,SQLXML4.0被推出來。SQLXML4.0已經成爲了一種成熟的數據訪問技術。

我們怎麼來看待SQLXML呢?它到底能爲我們作些什麼呢?我們可以把數據庫中的數據和XML數據看成是同一數據的不同表現形式。如果能過在這兩種數據表現形式之間提供一種Mapping,那麼我們就可以實現這兩種數據表現形式的轉換。換句話說,我們就可以同樣的數據從機遇關係數據庫的存儲形式,轉變成標記語言的XML格式。而SQLXML就是實現了這樣一種Mapping機制,並在此基礎上提供機遇XML(而不是純SQL)的數據操作方法。通過SQLXML,我們不但可以以XML的格式獲取查詢結果,我們還可以通過提過一個具有某種格式的XML實現數據庫的添加,刪除和修改;我們還可以提供一種有效的方式實現基於XML的數據批量上傳。

在這裏,我不打算做深入的介紹,只是通過提供一個簡單的例子,是大家對SQLXML有一個感性的認識。如果大家有興趣,我可以在後續的文章做詳細的介紹。

我們的例子是這樣的:在數據庫庫中,有兩張表T_ORDER和T_ORDER_DETAIL,用於存儲訂單和訂單明晰的信息。我們要做的是,通過SQLXML把相關數據已XML的形式取出,通過XSLT轉化成HTML,從而生成我們的Web Page。所以這是一個簡單的Web 應用。

表的結構,由於我們主要的目的在於介紹SQLXML,我們把業務邏輯和數據結構儘量精簡。


注:這是我比較喜歡的一種數據表的設計方式:爲每個表加上以下六個公共的字段——CREATED_BY,CREATED_ON,LAST_UPDATED_BY,LAST_UPDATED_ON,VERSION_NO,TRANSACTION_NO。前4個字段指明每條數據的建立和被最後寫該得人的時間,有利於敏感數據的追蹤和記錄Log。VERSION_NO是一個Timestamp類型的字段,用於判斷數據的併發。TRANSACTION_NO記錄的書該記錄的創建的更新屬於某個原子事務,有利於進行Audit Log。就以上面這兩個表爲例,如果我們設計的數據非常敏感,我們需要有一個機制來記錄每一次數據的創建和更新——操作時在什麼時候,操作者是誰,原來的數據是什麼,新的數據是什麼。那麼上面這樣的結構可以爲我們實現這樣的功能。如果有機會,我們給大家詳細的實現方式——我曾經爲原來的公司做過相應的設計和實現,我覺得其設計理念的實現對於一個企業級別的應用來說還是有很高的價值的。

跑題了,我們把話題拉回來。爲了大家能夠對我們實現的功能有一個感性的認識,我現在把我們應用涉及的兩個Web Page的Screen Shot先展示給大家。

Order.aspx: 列出所有的Order記錄,Order No.爲一個Link,通過它Redirect 到OrderDetail.aspx。


OrderDetail.aspx:列出當前Order的詳細信息。

我們現在就開始來一步一步得來實現這個簡單的應用。Source Code這裏下載

1. 建立一個Website,下面的這個Website的結構。

  • Utility.cs: 提供一個Common的方法通過SQLXML從Database中查詢數據。
  • Schema/Order.xsd: 這是一個被稱爲Mapping Schema的XSD。在SQLXML,Mapping Schema是最爲重要的對象,因爲所有基於SQLXML的操作都是建立在Database中的數據結構和XML有一個完全Mapping的基礎上的,而這樣的Mapping 就是通過Mapping Schema來實現的。
  • Template/Order_Sql.xml &Template/Order_Xpath.xml: SQLXML查詢允許我們把查詢的條件通過不同的方式傳遞到SqlXmlCommand(這個對象和ADO.NET忠德DbCommand有點相似,用於執行所有的Data Access 操作)——可以一純字符串的形式;可以一Stream的形式;可以把它們保存在一個Tenplate文件中,通過這個文件傳遞。本例就是採用最後一種方式。在這個例子中,我們會以兩種不同的方式來實現數據的查詢——通過For XML Select語句和通過Mapping Schema 結合Xpath。
  • Transform/Order.xsl & Transform.OrderDetail.xsl:由於SQLXML獲取的數據實際上是一個純XML,但是我們希望把數據在Web Page中顯示出來。所以我們需要通過這兩個XSLT把XML轉化成相應的HTML。
  • Order.css:應用於Web Page的Css,使得頁面看上去相對好看一點。
  • Order.aspx & OrderDetail.aspx:Web Page。

2. 創建Mapping Schema

<? xmlversion="1.0"encoding="utf-16" ?>
< xs:schema xmlns:sql ="urn:schemas-microsoft-com:mapping-schema"
xmlns:xs
="http://www.w3.org/2001/XMLSchema" >
< xs:annotation >
< xs:appinfo >
< sql:relationship name ="order_orderdetail" parent ="T_ORDER" parent-key ="ORDER_ID" child ="T_ORDER_DETAIL" child-key ="ORDER_ID" >
</ sql:relationship >
</ xs:appinfo >
</ xs:annotation >
< xs:element sql:relation ="T_ORDER" name ="order" type ="orderType" />
< xs:complexType name ="orderType" >
< xs:sequence >
< xs:element sql:relation ="T_ORDER_DETAIL" sql:relationship ="order_orderdetail" name ="product" type ="productType" />
</ xs:sequence >
< xs:attribute sql:field ="ORDER_ID" name ="id" type ="xs:int" />
< xs:attribute sql:field ="ORDER_DATE" name ="date" type ="xs:dateTime" />
< xs:attribute sql:field ="SUPPLIER" name ="supplier" type ="xs:string" />
</ xs:complexType >
< xs:complexType name ="productType" >
< xs:attribute sql:field ="PRODUCT_ID" name ="id" type ="xs:int" />
< xs:attribute sql:field ="PRODUCT_NAME" name ="name" type ="xs:string" />
< xs:attribute sql:field ="PRODUCT_NAME" name ="price" type ="xs:double" />
< xs:attribute sql:field ="QUANTITY" name ="quantity" type ="xs:int" />
</ xs:complexType >
</ xs:schema >

Mapping Schema是一個XSD,他實現瞭如何把數據庫中的對象(比如一個表,一個字段,甚至表與表之間的關聯)Mapping到XML中的某一格Element或者Attribute中。所有Mapping相關的Tag定義在這樣一個Namespace中——urn:schemas-microsoft-com:mapping-schema。通過relationship實現了T_ORDER和T_ORDER_DETAIL之間的關聯。通過relation把兩個表Mapping到一order和product XML Element上,通過field把數據庫中相關的字段Mapping到對應的XML Attribute上。仔細分析,上面的XSD實際上是定義了下面一種結構。

<? xmlversion='1.0'encoding="utf-8" ?>
< orders xmlns:sql ="urn:schemas-microsoft-com:xml-sql" >
< order id ="1" date ="2007-03-21T00:00:00" supplier ="DellCoporation" >
< product id ="1" name ="PC" price ="7000" quantity ="25" />
< product id ="2" name ="Laptop" price ="13000" quantity ="50" />
</ order >
< order id ="2" date ="2007-03-23T00:00:00" supplier ="HPCoporation" >
< product id ="3" name ="PC" price ="8000" quantity ="30" />
< product id ="4" name ="Printer" price ="3000" quantity ="5" />
</ order >
< order id ="3" date ="2007-03-25T00:00:00" supplier ="AACoporation" >
< product id ="5" name ="Pencil" price ="0.4" quantity ="3000" />
</ order >
</ orders >

3. 創建用於查詢的Template文件。

基於SQL的查詢——Template/Order_Sql.xml

<? xmlversion="1.0"encoding="utf-8" ?>
< orders xmlns:sql ="urn:schemas-microsoft-com:xml-sql" >
< sql:header >
< sql:param name ="orderID" > 1 </ sql:param >
</ sql:header >
< sql:query >
SELECT1ASTAG,
0ASPARENT,
dbo.T_ORDER.ORDER_IDAS[order!1!id],
dbo.T_ORDER.ORDER_DATEAS[order!1!date],
dbo.T_ORDER.SUPPLIERAS[order!1!supplier],
NULLas[product!2!id],
NULLas[product!2!name],
NULLas[product!2!price],
NULLas[product!2!quantity]
FROMdbo.T_ORDER
[email protected]@orderID=0
UNIONALL

SELECT
2ASTAG,
1ASPARENT,
dbo.T_ORDER.ORDER_ID,
dbo.T_ORDER.ORDER_DATE,
dbo.T_ORDER.SUPPLIER,
dbo.T_ORDER_DETAIL.PRODUCT_ID,
dbo.T_ORDER_DETAIL.PRODUCT_NAME,
dbo.T_ORDER_DETAIL.UNIT_PRICE,
dbo.T_ORDER_DETAIL.QUANTITY
FROM
dbo.T_ORDERINNERJOINdbo.T_ORDER_DETAIL
ONdbo.T_ORDER.ORDER_ID=dbo.T_ORDER_DETAIL.ORDER_ID
[email protected]@orderID=0
ORDERBY[order!1!id],[product!2!id]
FORXMLEXPLICIT
</ sql:query >
</ orders >

相信大家不會對這個感到陌生,我們通過在Select語句上運用FOR XML字句把原本已RowSet體現的結構轉換成一個XML。仔細分析這個Select漁具,你會發現而通過 上的方式獲得的結構是完全符合我們上面定義的Mapping Schema的。

接下來我們來通過第二種方式查詢——Mapping Schema結合XPath的Template 文件:Template/Order_Xpath.xml。

<? xmlversion="1.0"encoding="utf-8" ?>
< orders xmlns:sql ="urn:schemas-microsoft-com:xml-sql" >
< sql:header >
< sql:param name ="orderID" > 1 </ sql:param >
</ sql:header >
< sql:xpath-query mapping-schema ="http://localhost/Artech.OrderManagement\Schema\Order.xsd" >
/order[@id=$orderIDor$orderID='0']
</ sql:xpath-query >
</ orders >

我們通過sql:header定義一個參數OrderID,相應的查詢被置於<sql:xpath-query〉中()注意上面基於SQL的查詢對於的是<sql:query>。通過mapping-schema運用我們定義的Mapping Schema。通過一個XPath定義我們的查詢條件——如果傳入的參數時’0’)(雖然OrderID在DB中是Int,但是轉化成XML,我們不能區分它到底是Int還是String,而一般地,XML 把它當成Sring處理)則返回所有Order,否則返回對應ID的Order。

其實我們我們可以這樣來理解廣義的查詢——查詢時把篩選條件運用於某個具有預先知道的結構的數據集而獲得的複合你指定的篩選條件的數據集。首先查詢是機遇某種結構的,舉個例子,在電影院中,你可以通過你的電影票很快找到你所需要的座位,是因爲電影院是按照排和列來安置作爲的,同樣我們可以通過標準的SQL的Where語句很快地獲取我們需要的結果集,這是因爲所有的關係型DBMS都是同一種形如矩陣的結構來存儲數據的。我們可以通過XPath在XML中帥選我們希望的結果也是一樣的道理。所以對於我們可以通過XPath來從DB中查詢數據,便不會感到奇怪了——首先Mapping Schema通過Mapping機制實際上給我們提供了XML形式的DB,而XPath就像是XML DB中的Where子句一樣。

4. 創建XSLT

我們已經提到SQLXML查詢的結果只是一個單純的XML,要 把他們置於一個Web Page,我們需要把他們轉化成HTML,而且我們已經通過我們定義的Mapping Schema和SQL知道的將會生成的XML的Schema,要完成這樣的功能,我們很自然地想到XSLT。

<? xmlversion="1.0"encoding="utf-8" ?>
< xsl:stylesheet version ="1.0" xmlns:xsl ="http://www.w3.org/1999/XSL/Transform" >
< xsl:template match ="orders" >
< html xmlns ="http://www.w3.org/1999/xhtml" >
< head >
< title > OrderManagement </ title >
< link href ="Order.css" rel ="stylesheet" type ="text/css" />
</ head >
< body >
< table cellpadding ="0px" cellspacing ="0px" >
< tr >
< td class ="heading2" >
OrderNo.
</ td >
< td class ="heading2" >
Date
</ td >
< td class ="heading2" >
Supplier
</ td >
</ tr >
< xsl:apply-templates select ="order" ></ xsl:apply-templates >
</ table >
</ body >
</ html >
</ xsl:template >
< xsl:template match ="order" >
< tr >
< td >
< xsl:element name ="a" >
< xsl:attribute name ="href" >
OrderDetail.aspx?id=
< xsl:value-of select ="@id" />
</ xsl:attribute >
< xsl:value-of select ="@id" />
</ xsl:element >
</ td >
< td >
< xsl:value-of select ="@date" />
</ td >
< td >
< xsl:value-of select ="@supplier" />
</ td >
</ tr >
</ xsl:template >
</ xsl:stylesheet >

Transform/OrderDetail.xsl

<? xmlversion="1.0"encoding="utf-8" ?>
< xsl:stylesheet version ="1.0" xmlns:xsl ="http://www.w3.org/1999/XSL/Transform" >
< xsl:template match ="orders" >
< html xmlns ="http://www.w3.org/1999/xhtml" >
< head >
< title > OrderManagement </ title >
< link href ="Order.css" rel ="stylesheet" type ="text/css" />
</ head >
< body >
< xsl:apply-templates select ="order" ></ xsl:apply-templates >
</ body >
</ html >
</ xsl:template >
< xsl:template match ="order" >
< table cellpadding ="0px" cellspacing ="0px" >
< tr >
< td colspan ="2" class ="heading2" >
OrderNo.:
< xsl:value-of select ="@id" />
Date:
< xsl:value-of select ="@date" />
Supplier:
< xsl:value-of select ="@supplier" />
</ td >
</ tr >
< xsl:apply-templates select ="product" ></ xsl:apply-templates >
</ table >
</ xsl:template >
< xsl:template match ="product" >
< tr >
< td colspan ="2" >
ProductNo.:
< xsl:value-of select ="@id" />
</ td >
</ tr >
< tr >
< td width ="10%" > Name </ td >
< td style ="background-color:#ECF9EE" >
< xsl:value-of select ="@name" />
</ td >
</ tr >
< tr >
< td > Price </ td >
< td style ="background-color:#ECF9EE" >
< xsl:value-of select ="@price" />
</ td >
</ tr >
< tr >
< td > Quantity </ td >
< td style ="background-color:#ECF9EE" >
< xsl:value-of select ="@quantity" />
html>