最近研究了一些,也翻译了一些,贴出来和大家分享。dojo的更多内容可以上: www.dojocn.cn 上查看。
一下内容是原创,转载请注明:
dojo.data 是什么?
dojo.data 是一个统一的数据访问层,它没有数据库驱动的概念,只是一个统一的数据格式.
所有的数据被表现成一个 item 的 子item 或者 attribute,由于这样,数据就能够被统一的方式访问.
为了直接拿来就可以用,dojo.data提供了一个基础的 JsonItemStore 去读一个特定格式的JSON 数据.
DojoX 这个项目(或者说是模块)提供了更多的 stores (比如:XmlStore,CsvStore,OmplStore),这些stores可以解决服务器提供的对应的这些格式的数据.
此外,dojo.data是一个其他用户可以重写的API,这个概念类似java中的接口,因此,你可以编写一个定制数据格式的dojo.data的一个子集.
当你的定制好自己的特定格式,而且按照dojo.data的规范来做,widgets 就可以直接知道你的datastore,并且其他的程序可以读取你的数据,而不用了解你的特定格式.
dojo.data的最终目标是提供一个易用灵活的API集,就像interfaces.使datastore按照统一的格式被定制或者重写.
按照标准接口写的store就能够被多样的广泛的应用程序或者widget等等使用.
本质上,API隐藏了特定格式的数据,这些格式在JSON,XML,Csv或者其他数据格式,并且提供了一个一致的读item的attribute的方法,并且,用户可以根据实际的使用情况定制最优化的方法读取数据.(这一句翻译的不好)
你可以理解dojo.data是dojo.xhrGet()(这个封装了XMLHttp发送请求到服务器)上的一层,他们都是不用刷新页面的异步操作.但是xhrGet将能够读取任何MIME类型的数据并且一下子都返回,然后就依靠store去翻译这些数据成需要的格式,从而形成一般的读取模型.
综上:dojo.data提供了一套统一的读取数据的方法,它的目的就是提供一个标准,大家按照这个标准来开发读取数据的方法.
dojo.data对页面组件关系非常密切,比如Tree和ComBox都使用了store做为数据源.所以学好这个对灵活使用widget很有帮助
=========================================================================================================================
dojo.data 术语
dojo.data 术语类似关系型数据库术语,下面比较和对比了dojo.data术语和关系型数据库的术语:
dojo:datastore
db:cursor
描述:js对象通过dojo.data APIs从data source读取数据并且使 data item可用
dojo术语:data sorece
db术语:table
描述:这是保存原始数据的地方,比如:一个CsvStore数据源可能是.csv格式的文件,通常,data source是一个文件或者是个数据库服务器,或者是一个webservice或其他
dojo术语:item
db术语:row
dojo术语:attribute
db术语:column
描述:一个item的域或者属性.
dojo术语:value
db术语:-
dojo术语:reference
db术语:-
描述:一个item指向另一个item的值
dojo术语:identity
db术语:primary key
描述:主键
dojo术语:query
db术语:Where字句
描述:读取数据的条件.注意:非常建议所有的store使用attribute的name/value成对的结构,作为查询的格式连接stores.
internal data representation:一个私有的数据结构,datastore用它来缓存数据到客户端,比如XML DOM节点,匿名JSON对象或者数组
dojo术语:request
db术语:sql Select
描述:一个用来筛选item的参数,包括query,筛选attributes,大小写的限制和回调函数
=============================================================================================================================
dojo.data 设计和编程接口
在直接学习dojo.data的API之间,在研究API之前的基本概念必须先探究一下,因为一些设计上的决定被选择,但却没有解释为什么选择它们,可能会使这个设计看上去很奇怪。因此,在研究api细节之前,先完整的阅读这些概念:
概念1:数据访问被分成分离的APIs,store可以有选择的实现这些API
数据访问被破坏成分离的APIs,因为不是所有的服务和数据回传都需要所有的访问或者函数。因此不是所有的datastore能够实现比如read,write,identify,notifications这样的函数(注意这里的函数是js的function,在这里可以想像成java的接口)。
为了能够简单的就知道这个store提供什么特性,每一个store必须提供getFeatures()这个函数,这个函数报告出这个store使用了哪些APIs。
下面是基础的APIs定义:
dojo.data.api.Read:能够从一个dataitems里读取子items和attribute。而且可以查询,排序,过滤数据。
dojo.data.api.Write:能够生成、删除、更新dataitems和attribute。不是所有的服务都允许修改数据项的。实际上,大多数公共的服务,比如:Flikr,DelicIoUs还有googleMaps都只是提供读取服务。
dojo.data.api.Identity:能够通过唯一标识定位查找一个数据项,不是所有的数据服务有唯一标识的。
dojo.data.api.Notification:能够将发生在store上的数据修改事件通知给监听器。基础的功能是那些生成、删除、更新一个数据项的事件。对于像定期将数据更新的事件通知backend服务有显著的用处。
将来的一些特性:
下面的特性是一些dojo开发团体将会定义的额外的特性,这些都还没有完全明确,并且还在开发中。因此,他们没有被提供在当前版本的开发包中。注意啊,下面的列表可能随时变化。
dojo.data.api.Schema
dojo.data.api.Attribution
dojo.data.api.Versioning
dojo.data.api.Derivation
概念二:数据项和数据项的属性经常通过store的函数来访问,修改,创建和删除,而从来不直接访问。
这个概念就是防止dojo.data在最初的时候表现出来混乱,下面的例子显示了这个概念:
---------------------------------------------------------------------------------
var store = new some.data.Store();
var items;
.......
//假设这时items已经通过store.fetch()得到了数据,成为了array
//为了列举出这个数组的值,你要这样做:
for(var i=0;i<items.length;i++){
var item=items[i];
console.log("For attribute 'foo' value was:["+store.getValue(item,"foo")+"]");
}
----------------------------------------------------------------------------------
为什么不像下面这样做呢?
var value=item["foo"];
var value=item.foo;
var value=item.getValue("foo");
为什么这是dojo.data必须要求的呢?下面是原因:
1、访问效率。通过store的函数访问,store可以隐藏item的内部结构,无论在什么特殊形式下的数据格式,item都能保持一个格式.比如:
item可能是一个XML元素,这种情况下,当调用store.getValue()时store将使用DOM的API来访问数据;另外一种情况下,如果item是个一个javascript结构,那么store将通过javascript的访问符号来访问数据.从最终用户的角度来看,这个访问都是一样的:store.getValue(item,"attribute"),因此提供了一个统一的感观去访问多样的类型数据.通过减少item的加载次数,也提高了访问item的效率,因为你不用每次都转换格式了.
2、store可以使用非常简洁的内部结构。这大大减少了程序员记忆每种数据格式的工作量。
3、能够提供懒加载的模式
=======================================================================================
The Read API
Dojo.data 中最基础的API(或者说是接口)就是Read API. 所有的Store都必须实现这个接口,因为所有的store都需要获取和处理item(数据项)。针对处理数据项,Read API被设计的非常灵活。Read API提供如下的功能:
l 查看datastore都实现了那些接口,具有什么功能。通过getFeatures()方法。
l 查看一个item所包含的所有属性而不需要了解这个item的格式。getAttributes(item)方法
n 比如:一个item记录着一个学生的信息。通过getAttribuets()方法,你能得到这个学生包括哪些信息,比如:姓名,学号,等等。
n Item的保存格式有很多中,有json或者csv或者数据库中保存。通过getValue方法,你就能得到一个item的属性的值,转换数据格式交给store来做。
l 查看所有的items的属性,看看有没有指定的值。
l 验证一个js Object是不是store产生的item.
l 查看一个item是不是被完全加载了,还是仅仅是一个需要被加载的根(stub). isItemLoaded()
l 加载一个stub item,这个方法就是懒加载的时候使用的。loadItem()
l 给一些items排序
l 为一个查询结果分页。(我觉得对非数据库型的查询也许会有用一些,至少能使操作方便一些吧)
一些例子:
1、 查看一个store支持那些APIs
var store = new some.Datastore();
var features = store.getFeatures();
for(var i in features){
console.log("Store supports feature: " + i);
}
2、 测试一个对象是不是一个item
var store = new some.Datastore();
if(store.isItem(someObject)){
console.log("Object was an item.");
}else{
console.log("Object was NOT an item.");
}
3、 列举一个item的所有属性
var store = new some.Datastore();
...
//Assume that someItem is an item we got from a load.
var attributes = store.getAttributes(someItem);
for(var i = 0; i < attributes.length; i++){
console.log("Item has attribute; " + attributes[i]);
}
4、 测试一个item是否包含特定的属性
var store = new some.Datastore();
...
//Assume that someItem is an item we got from a load.
var attributes = store.getAttributes(someItem);
for(var i = 0; i < attributes.length; i++){
console.log("Item has attribute; " + attributes[i]);
}
5、 得到一个item的label
var store = new some.Datastore();
...
//Assume that someItem is an item we got from a load.
var label = store.getLabel(someItem);
console.log("item has label: " + label);
//其实一个item的label也是一个item的属性,只不过是一个特殊的属性。在json的开始部分:label:”name” 这个就声明了,item的name属性就是item的label,所以通过getLabel就得到了name属性,也就等同于getValue(item,”name”);
6、得到一个store的所有数据
var pantryStore = new dojo.data.JsonItemStore({url: "pantry_items.json" } );
//Define the onComplete callback to write COMPLETED to the page when the fetch has finished returning items.
var done = function(items,request){
document.appendChild(document.createTextNode("COMPLETED"));
}
//Define the callback that appends a textnode into the document each time an item is returned.
gotItem = function(item,request) {
document.appendChild(document.createTextNode(pantryStore.getValue(item,"name"));
document.appendChild(document.createElement("br"));
}
//Define a simple error handler.
var gotError = function(error,request){
alert("The request to the store Failed. " + error);
}
//Invoke the search
pantryStore.fetch({onComplete: done,onItem: gotItem,onError: gotError});
//这里onComplete和onItem同时使用的时候onComplete的items参数是null,这点请注意。
6、 通过一个标识得到一个item
var pantryStore = new dojo.data.JsonItemStore({url: "pantry_items.json" } );
var pepperItem = pantryStore.getItemByIdentity("pepper");
if (pepperItem !== null){
alert('Pepper is in aisle ' + pantryStore.getValue(pepperItem,"aisle");
}
//这个标识在json的开头部分有声明, identifier: 'name' ,也就是说item的name属性是标识。
7、 通过条件获取数据
jsonItemStore.fetch({
queryOptions: {ignoreCase: true}, //忽略大小写
query: { name: "*pepper*",aisle: "Spices" },//name 和 aisle是属性的名称
onComplete:
...
});
8、 嵌套定义item
一个item可以包括多个子item就像一棵树一样。使用reference可以关联子节点。通过getValues()方法可以得到孩子节点的数组。
9、 分页
var store = new dojo.data.JsonItemStore({url: "pantryStore.json" });
var pageSize = 10;
var request = null;
var outOfItems = false;
var onNext = function(){
if(!outOfItems){
request.start += pageSize;
store.fetch(request);
}
};
var onPrevIoUs = function(){
if (request.start > 0){
request.start -= pageSize;
store.fetch(request);
}
}
var itemsLoaded = function(items,request){
if (items.length < pageSize){
outOfItems = true;
}else{
outOfItems = false;
}
...
}
request = store.fetch({onComplete: itemsLoaded,start: 0; count: pageSize});
10、 排序
var store = new dojo.data.JsonItemStore({url: "pantryStore.json"});
var sortKeys = [
{attribute: "aisle",descending: true},
{attribute: "name",descending: false}
];
store.fetch({
sort: sortKeys;
onComplete:
...
});
//When onComplete is called,the array of items passed into it
//should be sorted according to the denoted sort array.