基於 XML Schema 的資料儲存方案

genusBIT發表於2009-03-30
對於一些小型專案,需要儲存的資料欄位和資料量可能都比較小,為了降低專案成本,提高專案的獨立性,開發人員希望能不依賴資料庫進行開發。本文提供了一種替代資料庫進行資料儲存的解決方案,該方案是應用 XML Schema 技術加以實現,本文會對實現的方法和細節進行詳細的講解。

引言

對於一些小型的專案,需要儲存的資料欄位和資料量可能都比較小,為了降低專案成本,或提高專案的獨立性,開發人員希望能不依賴資料庫進行開發。此時,利用 XML Schema 進行資料儲存便是一個非常好的解決方案。利用此方法可以直接把資料儲存在 xml 檔案中,然後把 xml 檔案存放在磁碟的某個位置,這樣會使得專案的部署與執行非常的方便。

本文會詳細的介紹如何基於 xml schema 進行資料的儲存,如何以面象物件的方式對 xml 檔案進行操作,以提高專案的開發速度和準確度。 文中還提供了一些詳細的程式碼示例來幫助讀者瞭解開發的技術細節。

Schema 資料儲存概述

利用 XML Schema 儲存資料的原理就是將資料儲存在 schema 所定義的 xml 文件中, 但本文中所講述的實現方法不會直接面對 xml,而是通過一些類以面象物件的方式實現存取操作,這些類是利用開發工具自動的生成的。本文會以 WID (Websphere Integration Development) 為參考,詳細介紹如何構建一個 XML Schema,如何根據 schema 生成 Java 類,以及如何應用這些類進行資料的存取操作。總體來說,應用 schema 進行資料儲存大致分為以下幾個步驟:

  1. 建立一個合適的 schema 檔案,來滿足資料的儲存需求
  2. 根據 schema 生成 Java 類,應用這些類以面象物件的方式對 xml 檔案進行操作
  3. 對 xml 檔案的儲存。

本文會對上述三個步驟來進行詳細的講解。

Schema 的構建原則

應用 schema 儲存資料首先要建立一個 schema 檔案,schema 中要包含所有需要儲存的資料欄位。那麼在建立 schema 的時候要考慮這些欄位的組織結構,要使得這些欄位的結構更為合理,使得後續的操作更為方便。對資料欄位應加以分類,然後針對每類資料建立一個結構化物件,就象在資料庫中設計表一樣,需要決定把哪些欄位放在一個表中。在 schema 中,與資料庫表相對應的物件是“ Type ”,每個“ Type ”中封裝了一類資料欄位。以一個簡單的庫存管理系統為例,為了實現這個系統,大概有五類資料需要儲存:貨物類別,每日售貨記錄,供應商資訊,送貨資訊,退貨資訊。在設計 schema 的時候,要針對每類資料建立一種 Type,以更加清晰的管理每種資料。


Schema 的建立方法

Schema 的建立可以有多種方式,既可以用文字工具手工編寫, 也可以藉助開發工具更快捷的建立。 WID 當中有一個建立 schema 的功能,可以應用它很方便的建立一個 schema 。在 WID 選單中選擇 : new – > other,在出現的對話方塊中,選擇 : XML – > XML Schema,根據圖中所示的步驟可以建立一個 schema 檔案。下圖所示為一個已經建立完成的 schema 檔案,該 schema 中包含了上文提到的庫存管理系統的五類資料(本文後續部分會以此 schema 為例進行講解)。


圖 1. schema 編輯器
schema 編輯器

上圖所示為 WID 中的 schema 編輯器,它有左右兩個編輯域:Elements 和 Types 。

1. Types 域是用來定義結構化物件的,把一些資料欄位封裝在其中。在編輯域中點選右鍵可以選擇增加或刪除一個 Type 。雙擊一個已經建立的 Type 會進入到 Type 的編輯器來對 Type 進行編輯。如下圖所示:


圖 2. type 編輯器
圖 2. type 編輯器

在此編輯器中可以為一個 Type 新增或刪除欄位,修改欄位型別。欄位型別可以是基本型別,如 string,date ;也可以是已經定義的其他 Type 型別,如 Category 物件中的 supplier 元素的型別就是一個已定義的 Type 型別。 Type 編輯器提供了一個 Properties 域,可以利用它對欄位方便的設定多種屬性。

在圖 1 所示的 Type 中有一個叫 StorageInfo 的 Type,它與其他五個 Type 有所不同,其他五個 Type 是用來封裝資料欄位的結構化物件,而 StorageInfo 是用來封裝這五個 Type 的,而且 StorageInfo 這個物件是必不可少的。在封裝這五個 Type 的時候要結合實際情況對他們組織一個合適的結構。可能只是簡單的把它們放在第一層,也可能按照引用關係來排列一個多層次的結構。下圖所示為 StorageInfo 的組織結構,本例中是把五個 Type 放在了第一層:


圖 3. 物件組織結構
圖3. 物件組織結構

2.Elements 域是用來為 schema 定義一個根物件,其他的物件是通過這個根物件來獲取的,但 Elements 域中的根物件只是一個簡單簡單的介面,需要為它指定一定 Types 域中的物件。圖 1 中所示的 StorageInfo 物件便是供 Elements 域中的 StorageInfo 使用的。根物件的定義如下圖所示:


圖 4. 根物件組織結構
圖4. 根物件組織結構 

Java 類的生成

為了實現以面象物件的方式操作 XML,需要根據 schema 來生成 Java 類。在 WID 中,根據 Schema 生成 Java 類非常簡單,右健單擊 schema 檔案,在出現的選單中選擇 Generate -> Java,之後會出現一個對話視窗,如下圖所示:


圖 5. Java 類生成器
圖5. Java類生成器

點選圖中的 Next,會提示選擇用來存放生成 Java 類的位置,之後點選 Finish,這樣 Java 類便生成了。下圖所示便為生成的所有的 Java 類。在 script. 目錄下面的類,以及 impl 和 util 兩個目錄中包含了這些類,應用這些類可以輕鬆的操作 XML 檔案。


圖 6. Java 類結構
圖 6. Java 類結構

由 WID 所生的類看起來似乎比較複雜,但它們有各自的用途,知道了它們的用途後便可以對它清晰的分類:

  1. Type 所對應的 Bean 類

    WID 會為圖 1 中所示的每個 Type 定義一個相應的 JavaBean,例如:Catetory.java,DailyRecord.java,DeliverInfo.java 分別對應 schema 中的 Catetory,DailyRecord,DeliverInfo 。但這些類都只是介面,其中只有 getter,setter 方法的定義,並無具體實現。

  2. Bean 的實現類

    Impl 目錄中放的是 Bean 的實現類,對每一個介面都會有一個具體的實現。

  3. DocumentRoot

    它表示 schema 的根物件,通過這個類可以得到 schema 中所有其它的物件,如 Category,DeliverInfo 等。

  4. ScriptFactory

    用來建立所有的 Bean 物件,如 createStorageInfo(),createDeliverInfo().

  5. Util 類

    Util 目錄中有六個類,但只有其中的 ScriptResourceUtil.java 類會被經常應用,其他的五個類很少會用,所以不必太在意他們。 ScriptResourceUtil 是一個比較重要的類,會用它進行把 xml 檔案載入到記憶體並轉化成 Java 物件,同是也要用它把 Java 物件儲存到 Xml 檔案中。


應用講解

1. 獲取根物件

在本文開頭已闡述過,被儲存的資料最終是存放在一個 xml 檔案中,而且對 xml 檔案的操作可以應用面象物件的方式進行。要實現以面象物件的方式操作 xml,首先要對 xml 檔案載入並加以轉換,然後獲取根物件,這樣便可取得所有其他的物件,對它們進行修改、刪除或建立。獲取根物件的程式碼示例如下,附件中包含了完整的程式碼,可以下載以供參考:


清單 1. 獲取文件根物件
				public StorageInfo loadStorageInfo() { 
 StorageInfo storageInfo = null; 
 try { 

 // 檔案輸入流指定到儲存資料的 xml 檔案
 InputStream is = 
  new FileInputStream ("D:/storageInfo/storageInfo.xml"); 

 // 利用 ScriptResourceUtil 來從輸入流中載入 xml 檔案,
 // 它會自動的 把 xml 檔案轉換為 DocumentRoot 物件 
 DocumentRoot root = 
  ScriptResourceUtil.getInstance().load(is); 

 // 從 DocumentRoot 中可以得到根物件,也就是 :StorageInfo 
 storageInfo = root.getStorageInfo(); 
 } catch (Exception e) { 
 e.printStackTrace(); 
 } 
 return storageInfo; 
 }

得到 StorageInfo 物件之後,便可以以面象物件的方式進行資料的新增,刪除,修改等操作。

2. 資料的操作

得到了根物件之後便可以進行資料的增刪改等操作。下面的示例講解了具體的應用。

新增一個 Category 記錄


清單 2. 增加物件
				public void addCatetory(StorageInfo storage) { 

 // 首先獲取 ScriptFactory,它是用來生成各種物件的工廠類 
 ScriptFactory factory = ScriptFactory.eINSTANCE; 

 // 利用工廠類建立一個新的 Category 物件 
 Category category = factory.createCategory(); 
				 
 // 為物件中的欄位賦值 
 category.setCostPrice(500); 
 category.setDiscountPrice(400); 
 category.setGoodsAmount(10000); 
 category.setName("Clothes"); 

 // 將新 Category 物件新增到根物件 StorageInfo 中 
 storage.getCategory().add(category); 

 }

修改物件

根據某個條件修改物件。例如想要修改名稱為“ Clothes ”的一個 Category 記錄,可以按如下示例進行操作:


清單 3. 修改物件
public void modifyCatetory(StorageInfo storage) { 

 // 得到所有的 Category 物件 
 List categories = storage.getCategory(); 

 // 應用迴圈得到指定的 Category 物件 
 for(int i=0; i

刪除物件

根據某個條件刪除一個物件。例如想要刪除名稱為“ Clothes ”的一個 Category 記錄,可以按如下示例進行操作:


清單 4. 刪除物件
public void deleteCatetory(StorageInfo storage) { 

 // 得到所有的 Category 物件 
 List categories = storage.getCategory(); 
 Category category = null; 

 // 應用迴圈得到指定的 Category 物件 
 for(int i=0; i

3. 物件儲存

在對根物件 StorageInfo 物件操作完成後,需要對其進行持久化,以免資料的丟失。資料的持久化也就是將 StorageInfo 轉換為 xml 並加以儲存,這個步驟同樣是應用 ScriptResourceUtil 這個類加以實現,見如下示例:


清單 5. 物件儲存
public void saveStorageInfo(StorageInfo storageInfo) { 
 try { 
 
 // 建立一個 DocumentRoot 物件 
 DocumentRoot docRoot = ScriptFactory.eINSTANCE.createDocumentRoot(); 
				 
 // 將需要儲存的 StorageInfo 設定在 DocumentRoot 中 
 docRoot.setStorageInfo(storageInfo); 

 // 定義一個輸出流 ,ScriptResourceUtil 會把 StorageInfo 寫到輸出 流中 

 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 
 ScriptResourceUtil.getInstance().save(docRoot,baos); 

 // 通過 FileOutputStream 把 StorageInfo 寫到 xml 檔案中 
 FileOutputStream fos = new FileOutputStream( "D:/storageInfo/storageInfo.xml"); 
 fos.write(baos.toByteArray()); 
 } catch (Exception e) { 
 e.printStackTrace(); 
  } 
  }

XML 檔案的加密

前面的示例中是把資料的內容直接存放到了 xml 檔案中,這個檔案是可讀的,如果這不慎外洩可能會造成機密資料的洩露,可能會給公司造成一定的損失。所以避免這種情況,需要把 xml 檔案進行加密,別人即使得到了 xml 檔案,也無法理解其中的內容。在加密 xml 檔案時,可以採用 base64 的方式,如下示例是經過修改的程式碼,它在儲存和載入時都是針對一個已加密的 xml 檔案。

1. 從加密的 Xml 檔案獲取根物件

因為 xml 檔案在儲存的時候已經應用 Base64 對其進行了加密,所以在載入檔案的時候需要對其進行解密。


清單 6. 從加密的 XML 檔案獲取根物件
				public static StorageInfo loadStorageInfo() { 
 StorageInfo storageInfo = null; 
 try { 
 InputStream is = new FileInputStream("D:\\storageInfo\\storageInfo.xml"); 

 // 應用 Base64 對加密過的內容進行解密 
 byte[] contents = new BASE64Decoder().decodeBuffer(is); 
 ByteArrayInputStream bais = new ByteArrayInputStream(contents); 
 DocumentRoot docRoot = ScriptResourceUtil.getInstance().load(bais); 
 storageInfo = docRoot.getStorageInfo(); 

 } catch (Exception e) { 
 e.printStackTrace(); 
 } 
 return storageInfo; 
 
 }

2. 將物件儲存到加密的 XML 檔案中

儲存的時候用 Base64 對內容進行加密


清單 7. 將物件儲存在加密的 XML 檔案中
public static void saveStorageInfo(StorageInfo storageInfo) { 
 try { 
 DocumentRoot docRoot = ScriptFactory.eINSTANCE.createDocumentRoot(); 
 docRoot.setStorageInfo(storageInfo); 
 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 
 ScriptResourceUtil.getInstance().save(docRoot,baos); 
 
 // 應用 Base64 對內容進行加密 
 byte[] encryptContents = new 
   BASE64Encoder().encode(baos.toByteArray()).getBytes(); 
 FileOutputStream fos = new FileOutputStream( "D:/storageInfo/storageInfo.xml"); 
 fos.write(encryptContents); 

 } catch (Exception e) { 
 e.printStackTrace(); 
 
 } 
 }

歷史資料的歸檔

應用程式在執行一段時間後,儲存的資料會越來越多,儲存資料的 xml 檔案也會隨之越來越大。如果 xml 檔案過大會影響程式的執行效率,延長程式的響應時間,因此每隔一定的時間應該對一些歷史資料進行歸檔。比如本文中的“每日售貨記錄”。這個記錄每天都會產生幾百甚至幾千條資料,那麼隔一段時間應該對這些資料進行歸檔。對這些資料進行歸檔比較簡單,通過寫一段程式碼便可以實現。如下是程式碼示例:


清單 8. 歸檔歷史資料
public void archiveHistoryData(StorageInfo storageInfo){ 
 try { List historyData = new ArrayList(); 

 // 根據銷售時間找到需要歸檔的 DailyRecord 物件 
 List dailyRecords = storageInfo.getDailyRecord(); 
 for(int i=0; i

注意事項

XML 檔案的儲存位置不是一程不變的,因此在程式開發時,不要把 xml 檔案儲存路徑直接寫在程式碼中,而是應該把它放到配置檔案中,這樣當變更 xml 儲存位置時,只需要改變一下配置檔案中的路徑就可以了,不必修改程式碼。

在設計 schema 時應儘量保持 schema 的簡結,不要設計太深的層次,否則在操作時會帶來不必要的麻煩。

應該定期做一下歷史資料的歸檔,以減小 xml 檔案大小,來提高程式的執行效率。

結束語

在一些情況下適當利用 schema 儲存方案不但能使專案更加獨立,不必依賴資料庫的支援,也可以提高程式的執行效率。 雖然利用 XML 儲存資料並不是一個新穎的方法,但是本文中所講的內容會讓讀者瞭解到一個新的 xml 儲存資料的實現方法。



來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/14751907/viewspace-580641/,如需轉載,請註明出處,否則將追究法律責任。

相關文章