簡介
PHP 支持簡單的 Web 應用程序開發和部署環境。這是它得到普遍應用的原因之一。DB2 9的原生 XML 功能進一步簡化了開發過程。這種簡化體現在以下方面:
- 應用程序代碼較少,復雜性降低
- 較簡單的關系模式
- 更好地管理因為更改業務需要而發生的模式演化
在本文中,我們將在該基礎上進行構建並說明使用 DB2 原生 XML 功能簡化應用程序代碼和關系模式的有效性。還將講述業務需求更改對數據的影響(模式演化)以及對應用程序代碼和關系模式的影響。
為了說明我們的推理,將通過一個模擬在線商店的使用情景來進行說明,該商店向注冊客戶出售古董銀器。
我們將在該情景中說明的一些要點包括:
設置 PHP 環境的容易程度將 DB2 原生 XML 功能與 PHP 應用程序(包括用 PHP 和 XQuery 編寫的 Web 服務)集成的容易程度使用 XQuery、存儲過程和視圖將業務邏輯和數據轉換放到數據庫中。在該情景中使用的 DB2 XML 功能將包含以下方面:
按分列結構在列中存儲 XML 文檔使用 XQuery 進行搜索和發布在 DB2 存儲過程和視圖中支持 XML使用 XML 索引提高性能為突出使用 DB2 原生 XML 支持對 PHP 應用程序代碼和關系模式設計的影響,該情景將創建一個並行環境,該環境使用不包含任何 XML 功能的數據庫(例如,MySQL)。我們將研究這兩個環境在應用程序代碼、數據庫查詢和關系模式方面的差異。還將說明選擇特定代碼、模式或查詢以及備選方案(如果可能)的理由。
情景
該情景模擬向注冊客戶出售古董銀器的在線商店。因為該情景的一個目的是說明不同的數據庫環境以及它們對應用程序代碼的影響,所以我們將對兩個應用程序進行同時說明,一個應用程序使用 DB2 原生 XML,另一個應用程序使用類似 MySQL 的開放源碼 RDMS,具有有限的 XML 功能或沒有任何 XML 功能。
因此,訪問 Web 站點的客戶將看到一個包含兩個垂直面板的頁面。每個面板都將顯示同一應用程序的一個版本,提供相同的用戶體驗,但在後端使用不同的數據庫:
具有原生 XML 支持的 DB2其他 RDBMS(在本例中為不使用任何 XML 功能的 DB2)為顯示應用程序代碼的差異,每個面板進一步分為兩個水平框架,上面的框架顯示在線商店,下面部分顯示代碼段。當用戶進行任何操作(如單擊某一種類或產品圖像)時,上面部分都會生成一個新頁面。下面部分顯示創建此頁面所需的代碼。
圖 1. 示例應用程序面板
這將說明,雖然在任何一個應用程序中用戶的體驗沒有變化,但代碼復雜性發生了很大變化。這種對比將突出用 PHP 編寫的普通 SMB 應用程序使用 DB2 原生 XML 功能的好處。
注意:我們對此情景的假設是業務數據已經是 XML 格式的,盡管數據庫可能沒有任何 XML 功能。這將產生可用於數據庫的使用 XML 功能的 PHP 應用程序代碼(如簡單 DOM)。具有有限 XML 功能或無 XML 功能的數據庫將 XML 數據存儲為 CLOB/BLOB 數據類型,或分割到關系字段中。
在 Web 站點中浏覽時的功能和用戶體驗
Web 站點將為用戶提供索引,列出商店中所有可用銀器的種類和品牌。用戶單擊某一種類或品牌時,將顯示該種類或品牌的貨品列表。選擇列表中的任何貨品都會在頁面中顯示該貨品的詳細信息。用戶可以將這些貨品添加到購物車中。一旦用戶提交了訂單,將會創建采購訂單並根據此采購訂單向用戶提供發票。用戶可以隨時檢查購物車中的貨品。用戶還能夠得到他們過去已訂購的所有貨品的報告。
應用程序體系結構
圖 2 顯示了示例應用程序的基本體系結構。
圖 2. 應用程序體系結構
關系和 XML 模式
XML 文檔和模式
原生 XML 存儲不需要 XML 列與特定 XML 模式關聯。需要對插入到數據庫中的 XML 文檔進行的任何驗證都在插入語句中使用 SQL/XML 函數顯式地進行。附錄中包含 XML 文檔示例。
關系模式
對於這兩個數據庫,用於存儲這些 XML 文檔的關系模式將有所不同。
對於 DB2 原生 XML,將有三個表,每個表包含兩列。
圖 3. DB2 原生 XML 模式
對於無 XML 支持的 RDBMS,將有四個表,每個表包含多個列:
圖 4. 無 XML 支持的情景的關系模式
可以看出,與無 XML 支持的 RDBMS 相比,DB2 原生 XML 的關系模式非常簡單。
我們已經通過將采購訂單文檔存儲為 BLOB,在基本關系數據庫中盡量保持采購訂單表模式簡單。當查看生成訂單歷史記錄時,這種操作的作用將非常明顯。
DB2 PHP 驅動程序
在開始講述 PHP 應用程序代碼之前,我們先了解一下 PHP 的 DB2 驅動程序。ibm_db2 驅動程序支持兩種連接數據庫的方法:編目 和非編目。編目連接可以是本地數據庫(如果有 DB2 服務器在本地運行),也可以是遠程 DB2 服務器節點。第二種方法通常用於遠程非編目連接,需要構建連接字符串(類似於 JDBC URL)以建立非編目連接。以下代碼連接編目數據庫。(客戶機應用程序不需要知道或關注編目連接時本地的還是遠程的。)
$conn = db2_connect($dbname, $dbuser, $dbpass);
if(!$conn) {
echo db2_conn_errormsg();
die("Unable to connect to database!");
}
還可以使用 db2_pconnect 創建與數據庫的持久連接。調用 db2_close 時,持久連接實際將不被關閉,因為連接句柄將在請求中保留。有關 PHP 的 IBM DB2 驅動程序的詳細信息,請訪問 http://www.php.net/manual/en/ref.ibm-db2.php。在下面的代碼段中,假設 $conn 是有效連接句柄。
填充數據庫
在 Web 站點可以開通之前,需要使用客戶信息和產品目錄填充數據庫。對於我們的情景,將不詳細說明如何獲取此數據。假設其以 XML 文檔的形式包含在本地文件系統的文件中。下面顯示了連接數據庫和執行 SQL 插入語句所需的 PHP 代碼段示例。
DB2 Viper
因為每個產品文檔都包含產品 ID 屬性,我們需要使用 PHP 的 SimpleXml API 提取該 ID。
注意: 使用此 API 比操作 DOM 對象容易得多,該對象在 PHP 版本 5 之前是惟一選擇。
- 創建數據庫連接:
$conn =db2_connect($dbname, $dbuser, $dbpass);
- 從文件打開文檔,成為一個變量:
$fileContents = file_get_contents("products/p1.xml");
- 從此變量創建簡單的 XML 對象:
$dom = simplexml_load_string($fileContents);
- 從文檔中提取產品 ID:
$prodID = (string) $dom["pid"];
- 創建准備好的語句,將 XML 文檔插入數據庫中:
$stmt =db2_prepare($conn, "INSERT INTO xmlproduct VALUES (?, ?)");
- 將從文檔中提取的產品 ID 作為參數與文檔一起傳遞到查詢:
db2_execute($stmt, array($prodID, $fileContents);
注意插入數據到 XML 列中與插入數據到任何 CLOB 列中沒有區別。因為這個 DB2 新版本允許在插入時對 XML 數據進行隱式分析,我們不需要對傳入值顯式地調用 XMLPARSE。如果我們希望在 XML 標記周圍保留無關空格,則可以使用帶 RESERVE WHITESPACE 選項的 XMLPARSE 函數。
注意: 這些代碼段中的查詢都使用斜體表示,以區別於 PHP 應用程序代碼。
非 XML RDBMS 因為此數據庫不具有任何 XML 功能,產品文檔需要分解到兩個關系表中。關系模式和 XML 模式之間的映射信息將直接嵌入 PHP 應用程序代碼中。
- 首先加載文檔到 DOM 中:
$fileContents = file_get_contents("$products/p1.xml");
$dom = simplexml_load_string($fileContents);
- 現在將產品的單個元素分割到本地變量中:
$prodID = (string) $dom["pid"];
$prodName = (string) $dom->description->name;
$prodDetails = (string) $dom->description->details;
$prodPrice = (float) $dom->description->price;
- 每個產品的圖像 URL 需要存儲在單獨的圖像表中:
$images = array();
foreach($dom->description->images->image as $image) {
switch((string) $image[type']) {
case thumbnail':$prodImgThumb = (string) $image;
$prodImgAlias = (string) $image[alias'];
if(!$prodImgAlias) $prodImgAlias = NULL;
$stmt = db2_prepare($conn, "INSERT INTO sqlimages (Pid, Type, Alias, Location) VALUES (?, ?, ?, ?)");
db2_execute($stmt, array($prodID, thumbnail', $prodImgAlias, $prodImgThumb));
case full':
$prodImgFull = (string) $image;
$prodImgAlias = (string) $image[alias'];
if(!$prodImgAlias) $prodImgAlias = NULL;
$stmt = db2_prepare($conn, "INSERT INTO sqlimages (Pid, Type, Alias, Location) VALUES (?, ?, ?, ?)");
db2_execute($stmt, array($prodID, full', $prodImgAlias, $prodImgFull));
}
}
- 當前實現 ibm_db2 驅動程序不能適當地將 NULL 變量作為參數來處理,以便執行函數;因此我們使用一個非強制性解決方案:
if(!$prodBrand) $prodBrand = " ";
if(!$prodCategory) $prodCategory = " ";
if(!$prodImgFull) $prodImgFull = " ";
- 現在保存產品表中的產品信息:
$stmt = db2_prepare($conn, "INSERT INTO sqlproduct (Pid, Name, Details, Brand,
Category, Price, Weight, Size, Description) VALUES
(?, ?, ?, ?, ?, ?, ?, ?, ?)");
db2_execute($stmt, array($prodID, $prodName,
$prodDetails, $prodBrand, $prodCategory,
$prodPrice, $prodWeight, $prodSize, $fileContents));
創建主頁 主頁包含在線商店中所有可用產品的種類和品牌的索引。索引的右邊區域顯示所有貨品的列表。
圖 5. 主頁
創建種類和品牌的索引列表 索引通過查詢數據庫中所有產品的惟一種類和品牌的列表而創建。啟動應用程序時將創建此列表。
DB2 Viper - 首先創建 DB2 視圖,以使用 XQuery 列出種類,XQuery 在所有產品中循環並返回所有惟一種類:
CREATE VIEW Categories(Category) AS SELECT DISTINCT(XMLCAST(
XMLQUERY(for $i in $t/product/description/category return $i'
PASSING BY REF T.DESCRIPTION AS "t" RETURNING SEQUENCE)
AS VARCHAR(128))) FROM
xmlproduct AS t
- 現在從應用程序調用該視圖:
$stmt = db2_exec($conn, "SELECT * FROM Categories");
while(list($cat) = db2_fetch_array($stmt)) {
echo "
";}
這兩種情況中的應用程序代碼相似。創建 XML 數據視圖使我們可以輕松地查詢視圖,從而有助於從應用程序代碼理解產品 XML 的結構。需要更改視圖中的 XQuery 以查找品牌元素,同樣 SQL 調用也需要查看 Brand 列。
模式演化對索引列表的影響
根據客戶反饋,我們需要允許用戶浏覽站點,以查找鍍銀的貨品或由純銀制造的貨品。我們看一下向索引中添加子種類對以下各項的影響:XML 模式、關系模式、查詢和 PHP 應用程序代碼。
對 XML 模式和文檔實例的影響
向產品 XML 模式中的種類元素添加新屬性(catx)。產品的所有新 XML 文檔現在都用純銀或鍍銀適當地填充了此屬性:
Miscellaneous
對關系模式的影響 - DB2 Viper
這將不需要對關系模式進行任何更改,因為 XML 文檔存儲在單個列中。 - 非 XML RDBMS
在基本關系數據庫中,將需要更改產品表的模式,添加名為 catx 的另一列。這可能涉及刪除並重新插入所有產品文檔。
對查詢的影響 - DB2 Viper 創建索引所需的 XQuery 將發生變化以在條件中包含這個新屬性。同樣,用於根據索引中的選擇列出貨品 XQuery 也將發生變化來包括新條件。
- 非 XML RDBMS
插入語句將發生變化以包括新列。
創建索引所需的查詢將發生變化以在 WHERE 子句中包括這個新列。同樣,用於根據索引中的選擇列出貨品的查詢也將發生變化以包括新條件。
對應用程序代碼的影響 - DB2 Viper
應用程序代碼將沒有任何更改。 - 非 XML RDBMS
- 將需要額外的 DOM 代碼,以分割出子種類信息。
- INSERT 語句將需要額外的參數。
- 所有數據都有可能需要重新插入,這導致終端用戶有一段時間無法操作。
用戶單擊種類或品牌時列出貨品
用戶單擊特定種類或品牌時,將生成該種類或品牌中所有貨品的列表。列表中的每個貨品都有簡短描述和到縮略圖像的 URL。此列表顯示在主頁中並在其中進行格式設置。
圖 6. 某一種類中的貨品列表
DB2 Viper
在 DB2 中,XQuery 不僅創建列表,而且還將其轉換為 HTML 輸出,從而浏覽器可以直接使用。使用 XQuery 的此功能,不僅可以推出業務邏輯,而且可以發布到數據庫服務器,從而有效地使中間層應用程序非常簡單。這正是使用 PHP 而不使用 Java™ 或 VS .NET® 的原因。
$xquery =for $i in $t/product
let $thumb := $i/description/images/image[@type="thumbnail"]
where $i/description/category = " . htmlentities($category) . "
return