轉自:[url]http://www.cnblogs.com/happyPawpaw/articles/2529490.html[/url]
[b]Dojo Data Store——統一資料通路接口[/b]
作者 王強 釋出于 2011年3月7日
無論在傳統的桌面應用還是在主流的網際網路應用中,資料始終占據着軟體應用中的核心地位。當下,web2.0已經是一個讓人們耳熟能詳的詞彙,而由此帶來的資料的開放與共享,引領我們走入了海量資料時代。在今天的網際網路上,資料的互動幾乎成為了我們的終極訴求,可随之而來的資料多樣性,資訊的分布式存儲及松耦合,以及資料量的幾何級規模的膨脹也帶來了資料組織上的難度的增大,與此同時,伴随着Ajax, RIA及面向服務的網絡應用的發展,其所要求的用戶端資料處理邏輯的複雜性不斷增加,使得開發難度不斷加大。出于簡化資料處理邏輯,增加應用的可維護及可擴充性的需求,目前流行的JavaScript架構也基本都會具有各自的資料處理子產品或接口。本文的目的就是為了介紹Dojo的資料處理子產品:Dojo.data。作為Dojo的資料進行中間層,其主要的職責就是解析及管理由資料源傳入的各種類型的資料,通過統一的資料通路與處理接口與資料展現層(Dojo Widget)進行通訊,便于各個Widget的管理與程式的移植。
Dojo Data中的資料管理
在面向服務應用大行其道的今天,協調資料的多樣性是開發網際網路應用中不可避免的首要問題。我們常見的資料格式包括Json, XML, Csv等,作為資料處理的中間層,能夠讓使用者以統一的接口連接配接不同的資料源是一個基本需求。在Dojo.data子產品中,預定義了不同的DataStore用于通路管理不同資料格式的資料源,而所有的DataStore都會實作相同的資料通路接口,這樣就可以成功實作資料提供層與資料展現層之間的松耦合。表1中列出了Dojo中部分已實作的各種不同的DataStore。
表1. Dojo中部分已實作的DataStore
DataStore 描述
dojo.data.ItemFileReadStore 用于JSON資料的隻讀的DataStore
dojo.data.ItemFileWriteStore 用于JSON資料的可讀寫的DataStore
dojox.data.CsvStore 用于CVS資料的隻讀的DataStore
dojox.data.OpmlStore 用于OPML(Outline Processor Markup Language)資料的隻讀的DataStore
dojox.data.HtmlTableStore 用于HTML table資料的隻讀的DataStore
dojox.data.XmlStore 用于XML資料的可讀寫的DataStore
dojox.data.FlickrStore 用于讀取flickr.com提供的資料的隻讀的DataStore。是一個很好的web service相關的DataStore的示例
dojox.data.QueryReadStore 用于讀取由伺服器端提供的JSON資料的隻讀的DataStore
盡管讀取的資料源多種多樣,但在DataStore中,通過統一資料通路接口,對資料的組織管理是一緻的。每條資料項都被作為一個item對象,其中包含了一定的鍵(attribute)值(value)對用以對應資料條目中的各個屬性值。下面以一段簡單的JSON資料片段為例,來介紹這種對應關系:
{
identifier: 'id',
label: 'name',
items: [
{ "id": "AF", "name":"Africa", "type":"continent",
"population":"900 million", "area": "30,221,532 sq km" },
{ "id": "AS", "name":"Asia", "type":"continent",
"population":"1 billion", "area": "25,428,192 sq km" }
]
}在這段JSON資料中共有兩條資料項(item),分别都包含有"id", "name", "type", "population"與"area"五個屬性字段。
Dojo.data 組織架構
為了符合各種應用中對資料中間層的不同需求,Dojo.data包對資料通路處理接口進行了一定程度的劃分,包括 read,write,identify,notifaction 等。各種DataStore可以根據其應用需求實作特定的接口。
表2. Dojo.data.api主要接口
Dojo.data主要接口 描述
Dojo.data.api.read 提供讀取資料項或者其屬性值的功能,同時也支援對資料集的搜尋,排序,和過濾。
Dojo.data.api.write 提供建立,删除,更新資料項功能。
Dojo.data.api.identify 提供基于唯一的标示符來定位和查詢資料項的功能。
Dojo.data.api.notification 提供當 datastore的資料項改變等事件發生時通知偵聽器的功能。最基本的事件包括資料的建立,修改和删除等。這也是Dojo.data的一項很重要的功能,通過此接口可以将資料展現層與資料中間層更好的分離開來。
Dojo.data API簡介
Read
資料的擷取是資料中間層的核心,Dojo.data.Read接口為異步擷取異構資料提供了很大的便利性和靈活性。在Read接口中,主要是通過異步方式進行資料的擷取,同時也提供了資料的排序、分頁、簡單查詢等基本功能的支援。
•fetch: function( keywordArgs)
fetch方法可以說是Dojo.data包的核心方法,它主要采用異步方法來擷取資料。該方法接收一個鍵值對對象參數,使用者可以通過對此參數中各個屬性進行指定以擷取特定的資料集合,如分頁,簡單查詢過濾,排序等。以下是部分主要的參數屬性介紹:
•onBegin與onComplete: fetch方法是采用異步的方式來進行資料的擷取,使用者可以通過onBegin與onComplete這兩個參數指定fetch方法的資料擷取回調函數,onBegin在資料傳回前會被調用一次,傳入兩個參數,分别為應傳回資料集的條目數及此次fetch的request對象;而onComplete方法是作為資料傳回的回調函數,資料集作為第一個參數傳入給該回調函數。
•start與count: 通常來說幾乎所有的實際應用都會要求分頁傳回資料以提供更好的使用者體驗,start和count這兩個屬性就是為支援分頁功能而實作的。start用于指定傳回資料的起始索引(由0開始),而count則用于設定傳回的資料條目數。
•query: 除了分頁以外,按需傳回特定的資料集也是一項重要功能,在Dojo.data中,這一功能則是通過query屬性提供支援的。query的值一般可設定為一個鍵值對對象,“鍵”應被設定為資料條目中的某項屬性,而“值”則為條件指定。Dojo.data提供了精确比對與模糊比對(通配符:*為任意字元,?為單個字元)兩種方式對資料進行過濾,可以根據具體情況選擇使用。
•sort:由于可能出現多個Widget使用同一個DataStore,資料集并不會以特定的序列進行存儲,當需要進行排序時,可以通過sort屬性進行指定,DataStore則會相應的傳回符合條件的資料集。sort 參數不僅指定了要排序的字段,而且還必須指定排序的順序即升序還是降序。
dataStore.fetch({
// 設定擷取資料的起始位置
start: 0,
// 設定擷取資料的條目數
count: 25,
// 設定模糊過濾條件
query: {'name': *},
// 資料排序設定
sort: [{ attribute: 'name', descending: false }],
// 設定開始資料擷取的回調函數
onBegin: function(size, requestObj){...},
// 設定資料擷取完成後的回調函數
onComplete: function(items, requestObj){...},
// 設定資料擷取失敗後的回調函數
onError: function(error, requestObj){...}
});
•getValue: function(item, attribute, defaultValue)
用于擷取某個給定的資料項的某個屬性值,如果該條資料不含有指定的屬性,則傳回一個指定的預設值。item參數為給定的資料項,attribute參數為指定的屬性字段,defaultValue為可選參數。
var value = dataStore.getValue(item, 'name', 'no name'); •getAttributes: function( item)
擷取給定資料項的所有屬性字段,傳回值為一個數組。
Write
Dojo.data.Wirte接口主要提供了資料的更新功能API,包括建立、删除、更新資料。同 Read 接口類似,Write API 的設計目标也是屏蔽底層資料存儲格式的差異,為使用者提供統一的資料通路 API。借助這些 API,使用者可以專注于業務層面的邏輯實作,而無需花費太多精力去關注底層資料的存儲格式。
•newItem: function( keywordArgs, parentInfo)
在DataStore中新建立一個資料項。第一個參數為一個鍵值對對象,用于設定新建立的資料項,第二個參數為可選參數,當使用者想将新建立的資料項作為某個已存在的資料項的子,則可以通過這個參數進行設定。具體應用請參照下面的小示例:
var euItem = {"id": "EU", "name":"Europe", "type":"continent", "children": [] }
// 建立資料項
dataStore.newItem(euItem);
// 建立子資料項
dataStore.newItem({"id": "GM", "name":"Germany", "type":"country"}, {parent: euItem, attribute: "children"});
•deleteItem: function( item)
在DataStore中删除指定的資料項。
•setValue: function( item, attribute, value)
更新某條給定資料項的某個屬性值。
Notification
當DataStore中有資料更新時,相應的Notification中定義的監聽函數就會被調用。使用過Dojo的讀者可 能都會注意到,在Widget中一般不會有new、delete等其他JavaScript庫控件中常見的API。這是因為Dojo data的設計是力求将資料層與表現層進行分割,對資料的操作都集中在資料層進行控制,而資料集的改變也能夠自動的在應用控件上進行反映,這一功能就是當DataStore在進行資料更新操作時,通過Notification接口的通知作用實作的。
•onNew: function( newItem, parentInfo)
當DataStore中建立新資料項操作成功後被自動調用。newItem參數就是新建立的資料項對象,parentInfo是可選參數,用于描述新建立資料項的父資料項。
•onDelete: function( deletedItem)
當DataStore中删除某項資料項後被自動調用。deletedItem參數就是被删除的資料項對象。
•onSet: function( item, attribute, oldValue, newValue)
在DataStore的某項資料項被更新後進行調用。四個參數分别為資料項對象,被更新資料項屬性,該資料的原有值以及更新後的值。
Identify
很多資料源都會為資料提供唯一的辨別符,Dojo.data.Identify接口則提供了基于唯一辨別符進行資料擷取定位的API支援。
•fetchItemByIdentity: function( keywordArgs)
同Read接口中的fetch方法類似,此方法也是一個異步方法,使用者需要在參數對象中指定資料項擷取後的回調處理函數。keywordArgs參數是一個鍵值對對象,主要需要包括兩個屬性,一個是要進行指定擷取的資料項辨別符identify,另一個則是回調處理函數onItem。在指定identify的資料項擷取成功後,onItem回調函數則會被自動調用,以處理後續操作。
dataStore.fetchItemByIdentity({
// 指定要進行擷取的資料項的id
identity: "AS",
// 設定資料傳回後的回調函數
onItem: function(item){…},
// 設定錯誤回調函數
onError: function(error){…}
});•getIdentity: function( item)
此方法用于擷取給定資料項的辨別符。
DataStore應用
一般來說,Dijit中的各個小部件都提供了對DataStore的支援,當我們在使用某個Widget來進行資料展現時,通常我們隻需要根據資料源的格式類型來選擇好DataStore,然後在Widget聲明中對DataStore進行指定就可以了。下面我們就通過DataGrid及ComboBox作為資料展現UI,基于不同的資料格式為它們設定不同的DataStore。
以下是一份JSON資料:
data = {
identifier: 'id',
label: 'name',
items: [
{ "id": "AF", "name":"Africa", "type":"continent",
"population":"900 million", "area": "30,221,532 sq km" },
{ "id": "AS", "name":"Asia", "type":"continent",
"population":"1 billion", "area": "25,428,192 sq km" },
{ "id": "OC", "name":"Oceania", "type":"continent",
"population":"21 million", "area": "15,928,294 sq km" },
{ "id": "EU", "name":"Europe", "type":"continent",
"population":"56 million", "area": "25,928,294 sq km" },
{ "id": "NA", "name":"North America", "type":"continent",
"population":"100 million", "area": "90,928,294 sq km" },
{ "id": "SA", "name":"South America", "type":"continent",
"population":"102 million", "area": "78,928,294 sq km" },
{ "id": "AN", "name":"Antarctica", "type":"continent",
"population":"998", "area": "102,928,294 sq km" }
]};在這裡,我們采用比較簡單的dojo.data.ItemFileReadStore:
var jsonStore = new dojo.data.ItemFileReadStore({data: data}); ItemFileReadStore比較适合于處理資料量較小的資料源,資料源可以是一個JSON檔案或者象本例一樣直接指定到用戶端記憶體中的一組資料。當你使用更加大型的JSON資料集時,可以使用JsonRestStore,采用Rest服務來進行資料提供。
接下來,我們來聲明一個DataGrid。在這裡DataStore是通過”store”屬性進行設定的。
<table jsid="grid" store="jsonStore" query="{name:’*'}" dojoType="dojox.grid.DataGrid" class="grid">
<thead>
<tr>
<th field="name" width="auto">Name</th>
<th field="population" width="auto">Population</th>
<th field="area" width="auto">Area</th>
</tr>
</thead>
</table>
生成的DataGrid如下圖所示:
由于Dojo中對資料展現層與資料中間層的松耦合,同樣一份資料源可以在不進行任何處理的情況下為多個Widget提供資料,而且由于資料的過濾、排序、分頁都是根據資料擷取請求按需傳回的,使用相同 DataStore的多個Widget間也不會産生沖突。下面我們就以同樣的DataStore,為一個dijit.form.ComboBox提供資料:
<input dojoType="dijit.form.ComboBox" store="jsonStore" searchAttr="name"></input>
在很多實際應用中,可能會使用不同的資料源,下面,我們采用不同的資料格式,以XmlStore來替換ItemFileReadStore。首先将JSON資料轉換為XML資料格式:
<continents>
<continent>
<name>Africa</name>
<population>900 million</population>
<area>30,221,532 sq km</area>
</continent>
<continent>
<name>Asia</name>
<population>1 billion</population>
<area>25,428,192 sq km</area>
</continent>
<continent>
<name>Oceania</name>
<population>21 million</population>
<area>15,928,294 sq km</area>
</continent>
<continent>
<name>Europe</name>
<population>56 million</population>
<area>25,928,294 sq km</area>
</continent>
<continent>
<name>North America</name>
<population>100 million</population>
<area>90,928,294 sq km</area>
</continent>
<continent>
<name>South America</name>
<population>102 million</population>
<area>78,928,294 sq km</area>
</continent>
<continent>
<name>Antarctica</name>
<population>998</population>
<area>102,928,294 sq km</area>
</continent>
</continents>
XmlStore是一個用戶端的資料存儲器,用于讀取XML資料源。它由Dojo官方提供并包含在DojoX子項目中。XmlStore為基本的XML資料(一種常用的資料交換格式)提供讀/寫接口。XmlStore可以用于一般的XML文檔,是以非常有用。存儲器的設計是你可以通過覆寫其部分方法來自定義讀/寫資料的行為。下面的示例給出了如何建立XmlStore并将其應用到Grid及ComboBox中:
var xmlStore = new dojox.data.XmlStore({
url: ‘continents.xml’,
label: ‘name’
});
<table jsid="grid" store="xmlStore" dojoType="dojox.grid.DataGrid" class="grid">
<thead>
<tr>
<th field="name" width="auto">Name</th>
<th field="population" width="auto">Population</th>
<th field="area" width="auto">Area</th>
</tr>
</thead>
</table>
<input dojoType="dijit.form.ComboBox" store="xmlStore" searchAttr="name">我們幾乎不需要修改關于Grid和ComboBox的任何代碼,就能讓它們繼續工作。唯一需要做的改動,就是聲明一個資料源,并将它設定為grid的輸入。我們不需要操心任何關于資料擷取、解析、以及管理的事情,資料存儲器的API做了所有的工作。
可以看出,作為資料中間層,Dojo.data通過優秀的API設計充分達成了資料展現層與資料管理層之間的松耦合,同時統一的資料通路接口使得對多種資料格式的應用以及程式移植都帶來了相當大的便利性。
參考資料
•Dojo中文部落格
•Dojo參考文檔