基於 XML Schema 的數據存儲方案

引言html

對於一些小型的項目,須要存儲的數據字段和數據量可能都比較小,爲了下降項目成本,或提升項目的獨立性,開發人員但願能不依賴數據庫進行開 發。此時,利用 XML Schema 進行數據存儲即是一個很是好的解決方案。利用此方法能夠直接把數據存儲在 xml 文件中,而後把 xml 文件存放在磁盤的某個位置,這樣會使得項目的部署與運行很是的方便。java

本文會詳細的介紹如何基於 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 文件的保存。

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

 







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<categories.size(); i++){
Category category = (Category)categories.get(i);
if("Clothes".equals(category.getName())){

// 對 Category 進行修改
category.setCostPrice(450);
category.setDiscountPrice(260);
}
}
}

 

刪除對象

根據某個 條件刪除一個對象。例如想要刪除名稱爲「 Clothes 」的一個 Category 記錄,能夠按以下示例進行操做:


清單 4. 刪除對象

public void deleteCatetory(StorageInfo storage) { 

// 獲得全部的 Category 對象
List categories = storage.getCategory();
Category category = null;

// 應用循環獲得指定的 Category 對象
for(int i=0; i<categories.size(); i++){
category = (Category)categories.get(i);
if("Clothes".equals(category.getName())){
break;
}
}
// 刪除對象
categories.remove(category);

}

 

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<dailyRecords.size(); i++){
DailyRecord record = (DailyRecord)dailyRecords.get(i);
if(((Date)record.getSaleDate()).getTime() < System.currentTimeMillis()){
historyData.add(record);
}
}

// 從當前的 xml 中刪除須要被歸檔的 DailyRecord 記錄
dailyRecords.removeAll(historyData);

// 建立新的 StorageInfo 來保存歷史數據,而後將歷史數據進行保存
storageInfo = ScriptFactory.eINSTANCE.createStorageInfo();
storageInfo.getDailyRecord().addAll(historyData);
DocumentRoot docRoot = ScriptFactory.eINSTANCE.createDocumentRoot();
docRoot.setStorageInfo(storageInfo);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ScriptResourceUtil.getInstance().save(docRoot,baos);
byte[] encryptContents = new BASE64Encoder().encode(baos.toByteArray()).getBytes();
FileOutputStream fos = new FileOutputStream( "D:/storageInfo/archive.xml");
fos.write(encryptContents);
} catch (Exception e) {
e.printStackTrace();

}
}






 



注意事項

XML 文件的存儲位置不是一程不變的,所以在程序開發時,不要把 xml 文件存儲路徑直接寫在代碼中,而是應該把它放到配置文件中,這樣當變動 xml 存儲位置時,只須要改變一下配置文件中的路徑就能夠了,沒必要修改代碼。

在設計 schema 時應儘可能保持 schema 的簡結,不要設計太深的層次,不然在操做時會帶來沒必要要的麻煩。

應該按期作一下歷史數據的歸檔,以減少 xml 文件大小,來提升程序的執行效率。

 




 



結束語

在一些狀況下適當利用 schema 存儲方案不但能使項目更加獨立,沒必要依賴數據庫的支持,也能夠提升程序的執行效率。 雖然利用 XML 存儲數據並非一個新穎的方法,可是本文中所講的內容會讓讀者瞭解到一個新的 xml 存儲數據的實現方法。



參考資料

學 習

相關文章
相關標籤/搜索