數據綁定在今天的 java 技術和 xml 編程世界中雖然是老生常談,但仍然有很大的誤解。這個專欄拋開所有空洞的理論,把重點放在應用數據綁定需要了解的概念上。您將了解一般數據綁定和 XML 世界中的數據綁定之間的區別,以及往返、語義等價和對數據綁定軟件包的要求。 既然閱讀本專欄,就說明您至少對 XML 數據綁定有一定的愛好。若是在一年前,我就不得不定義數據綁定是什么,深入到復雜的概念,這是一項令人厭倦的工作,通常要花費幾頁的篇幅。不過現在是 2004 年,而不是一年以前了,基本上所有目前從事 XML 和 Java 開發的人員的思想中,數據綁定似乎已經扎下了根。由于某些原因,坦白地講,這令我感到困惑,甚至連 SAX 和 DOM 代表什么也不知道的 XML 新手都在天天使用數據綁定。因此對于您(單擊這篇文章標題的人)的背景知識,我并不像在過去所能做到的那樣肯定。
一般的數據綁定 首先,必須暫時放一放 XML 數據綁定(得承認,您滿腦子想的都是這個),來看看更一般意義上的數據綁定。雖然多數程序員都認為數據綁定就是 XML 文檔和某種 Java 編程結構之間的轉換過程,但這僅僅是數據綁定技術的一種具體應用。在展開這個復雜的問題之前,最好先看一看更廣闊的背景。
傳統的數據綁定 簡單地講,數據綁定是指取出一些數據(比如從 XML 文檔、文本文件或者數據庫中)并通過程序表示這些數據的過程??把數據綁定 到虛擬機(VM)能夠理解并且可以操作的某種內存中結構。相應地,數據綁定軟件包應該能夠用 VM 上修改的數據更新底層存儲媒介(XML 文檔、文件或數據庫分區)中的數據。假如連這些最少 的功能都不能實現,就稱不上數據綁定軟件包??至少從這個詞的傳統意義上講不是的。
// Get an instance of a data binding factory Factory factory = DBFactory.newInstance(); factory.connect(); List employees = factory.unmarshal(DatabaseConstants.EMPLOYEE_TABLE);
// Manipulate data in employee objects for (Iterator i = employees.iterator; i.hasNext(); ) { Employee employee = (Employee)i.next(); System.out.println("First name: " + employee.getFirstName()); System.out.println("Last name: " + employee.getLastName()); // etc... }
比數據庫更常見的是將此類 API 用于 XML,從 XML 文檔中提取數據。這就是問題的由來:程序員如此關注 XML 上的數據綁定而非一般意義上的數據綁定,以至于經常把通用 API 看作是針對特定格式的。比如,清單 2 中的代碼和 清單 1 是等價的,最近我發現很多咨詢建議中使用這類代碼。
清單 2. 使用數據綁定 API 訪問 XML 文檔
// Get an instance of a data binding factory Factory factory = XMLFactory.newInstance(); factory.connect(); List employees = factory.unmarshal(XMLConstants.EMPLOYEE_DOCUMENT);
// Manipulate data in employee objects for (Iterator i = employees.iterator; i.hasNext(); ) { Employee employee = (Employee)i.next(); System.out.println("First name: " + employee.getFirstName()); System.out.println("Last name: " + employee.getLastName());
非凡要注重粗體顯示的兩行??這里把 XML 語義引入到了實際的數據綁定應用中。這種做法非常糟糕,會令您急得手忙腳亂。在使用(和選擇)數據綁定 API 時,應該尋找通用的 API,而不是專用的 API。這就是為何我要花費您這么多寶貴的時間,介紹各種數據綁定 API 的互換性,來說明所謂的具體細節實際上是實現的細節,而不是 API 的細節 。假如在代碼中暴露 XML 語義和數據庫語義,就可能得到編寫很差、實現很差或者兩者都很差的數據綁定 API。從一種存儲媒介改為另一種存儲媒介應該很簡單,對代碼的影響很小。不要再做 Java 和 XML 數據綁定程序員了,開始轉變為數據綁定程序員吧。您會發現您的代碼和所實現的功能比過去更加健壯,更加靈活。
雖然 API 是專用的,但仍然要使用 一些人可能在這里已經看到了一線曙光,但是仍然要使用特定的數據綁定 API 或實現。您可能使用類似清單 2 的代碼,或者其他類似的東西,在不需要的的地方公開數據格式結構。那么好吧??實際上可以通過少量的工作來獲得前述的效果。這時應考慮編寫一個包裝程序 API。包裝程序 API 位于開發人員和數據綁定 API(它大概不能很好地完成自己的工作)之間。您可以包裝有問題的功能,比如 getAttribute() 或 getChild() 調用(這是 XML 專用的),編寫新的類調用這些方法,并使用更加有意義而且存儲中立的方法名。還應該考慮預備一個工廠類,以便很輕易從 XML 媒介轉到數據庫媒介(或者有一天可能會用到的其他媒介)。這不是一項輕松的任務,但有一定經驗的開發人員仍然可以完成。我將在這里說明如何創建包裝程序 API,但是我計劃在本專欄以后的文章中還會討論這個話題。
重要的定義 理解了通用數據綁定 API 的用處以后,您可能已經預備學習如何正確地選擇和使用數據綁定 API。在這篇入門介紹的最后,我們將學習一些您可能還不熟悉的定義。這些概念對于保證 API 的正確運行(尤其是在處理 XML 時)至關重要;了解這些定義還可以避免造成 API 輸出的 XML 和輸入不相符。
編組(Marshalling)是把內存中的數據轉化到存儲媒介上的過程。因此在 Java 和 XML 環境中,編組就是把一些 Java 對象轉化成一個(或多個) XML 文檔。在數據庫環境中,則是把 Java 表示的數據存入數據庫。顯然,編組的秘密在于把 Java 實例中的面向對象結構轉化成適用于 XML 的扁平結構,或者 RDBMS 中的關系結構(使用 Java 技術轉換到 OODBMS 實際上很簡單)。
解組(Unmarshalling)是把數據從存儲媒介轉換到內存中的過程??正好與編組相反。因此需要把 XML 文檔解組到 Java VM 中。這里的復雜性不是在扁平數據中,因為這不是必需的,而在于從正確的數據到正確的 Java 代碼變量的映射。假如映射是錯誤的,就不可能正確地訪問數據。當然,假如再嘗試重新編組還會造成更大的問題,并且問題傳播得很快。
往返 往返(Round-tripping)可能是最重要也最輕易誤解的數據綁定術語。往返用于描述從存儲媒介到內存然后回到存儲媒介的完整循環。在 XML 和 Java 技術環境中,這就意味著從 XML 文檔到 Java 實例變量,然后再回到 XML 文檔。正確的往返要求,假如中間沒有修改數據,XML 輸入和 XML 輸出應該是等同的。換句話說,清單 3 中的文檔 input.xml 和 output.xml 應該基本上相同。
清單 3. 往返
// Get an instance of a data binding factory Factory factory = XMLFactory.newInstance(); factory.connect(); List employees = factory.unmarshal("input.xml");
employees.marshal("output.xml");
假如輸入和輸出不能正確地匹配,就說明往返中存在問題,實際應用這種 API 很快就會陷入困境。假如把數據傳入 VM 內存時不能依靠 API 正確地保持數據,您基本上就陷入了絕境。下一篇文章中將分析 Sun JAXB API 的往返能力,看看是否能實現期望的功能。
語義等價 因為 XML 是數據的文本格式,它有一些有趣的怪癖。比方說,XML 處理(和忽略)空白的方式不是由自身決定的。是否存在 DTD 或者模式影響到空白的處理;CDATA 元素的使用影響實體處理;一個編輯器中的縮進編排格式到另一個編輯器中可能會成為一團亂麻。
這些還不夠,XML 對數據及其順序有非常非凡的規則。在 XML 文檔中,元素屬性的順序不重要。因為這種無序性和空白處理的不同,兩個看起來有天壤之別的文檔可能實際上包含完全相同的數據。在這種情況下,說兩個文檔“相同”或者“相等”實際上是不嚴格的。這就引入了另一個術語:語義等價。就是說,從文檔中所含數據和涉及的結構的角度來看,兩個文檔是相同。盡管文檔看起來可能彼此不同,但從數據的角度看是等價的。比如,看看清單 4 中的代碼段。
您可能已經看到,這兩個清單包含著同樣的數據,事實上它們在語義上是等價的。因為這個例子很簡單,所以等價性不難確定。但是,假如 XML 文檔有成百上千條記錄,每個元素都有多個屬性(都能以任意的順序出現),就很難判定語義上的等價性。這也是數據綁定框架中最困難的部分??想想上一節所述的往返。正確的往返不要求輸入和輸出文檔看起來一致,只要求它們在語義上是等價的。因此現在要指出的是,假如說 API 能正確地往返,這就要求您了解 XML 的基礎,并且保證您的 API 完全遵循這些規則。
雖然一些 API 提供了各種各樣的機制定制輸出,但為了保證語義等價,不一定要使用這種選項。和往返一樣,我也預備在下一篇文章中分析 JAXB 的語義等價處理,看看輸入和輸出文檔在結構上的區別。我還將考察文檔在解組和編組過程中發生的任何變化。
結束語 可能讀完了本文后,有些讀者感到它沒有講如何做什么事情。但是,我認為假如花點時間把握了數據綁定的基礎,非凡是往返和語義等價,您會發現在使用數據綁定 API 時,您可以編寫出更好的、最終也更快的程序。這也是深入學習下一篇文章??JAXB API 的基礎。同時,您也可以使用喜歡的 API 作一些簡單的測試,看看它是否(理想地)保持了語義等價性。好了,下一篇文章再見。