写点什么

Dojo Data Store——统一数据访问接口

  • 2011-03-07
  • 本文字数:6492 字

    阅读完需:约 21 分钟

无论在传统的桌面应用还是在主流的互联网应用中,数据始终占据着软件应用中的核心地位。当下,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(/* Object */ 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\*/item, /\*attribute-name-string\*/attribute, /\*value?\*/ defaultValue) 用于获取某个给定的数据项的某个属性值,如果该条数据不含有指定的属性,则返回一个指定的默认值。item 参数为给定的数据项,attribute 参数为指定的属性字段,defaultValue 为可选参数。

var value = dataStore.getValue(item, ‘name’, ‘no name’);

复制代码
- getAttributes: function(/\* item \*/ item) 获取给定数据项的所有属性字段,返回值为一个数组。
{1}
### Write
{1}
Dojo.data.Wirte 接口主要提供了数据的更新功能 API,包括创建、删除、更新数据。同 Read 接口类似,Write API 的设计目标也是屏蔽底层数据存储格式的差异,为用户提供统一的数据访问 API。借助这些 API,用户可以专注于业务层面的逻辑实现,而无需花费太多精力去关注底层数据的存储格式。
{1}
- newItem: function(/\*Object?\*/ keywordArgs, /\*Object?\*/ 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\*/ item) 在 DataStore 中删除指定的数据项。
- setValue: function(/\*item\*/ item, /\*string\*/ attribute, /\*almost anything\*/value) 更新某条给定数据项的某个属性值。
{1}
### Notification
{1}
DataStore 中有数据更新时,相应的 Notification 中定义的监听函数就会被调用。使用过 Dojo 的读者可 能都会注意到,在 Widget 中一般不会有 newdelete 等其他 JavaScript 库控件中常见的 API。这是因为 Dojo data 的设计是力求将数据层与表现层进行分割,对数据的操作都集中在数据层进行控制,而数据集的改变也能够自动的在应用控件上进行反映,这一功能就是当 DataStore 在进行数据更新操作时,通过 Notification 接口的通知作用实现的。
{1}
- onNew: function(/\*item\*/ newItem, /\*object?\*/ parentInfo) 当 DataStore 中创建新数据项操作成功后被自动调用。newItem 参数就是新创建的数据项对象,parentInfo 是可选参数,用于描述新创建数据项的父数据项。
- onDelete: function(/\*item\*/ deletedItem) 当 DataStore 中删除某项数据项后被自动调用。deletedItem 参数就是被删除的数据项对象。
- onSet: function(/\*item\*/ item, /\*attribute-name-string\*/ attribute, /\*object | array\*/ oldValue, /\*object | array\*/ newValue) 在 DataStore 的某项数据项被更新后进行调用。四个参数分别为数据项对象,被更新数据项属性,该数据的原有值以及更新后的值。
{1}
### Identify
{1}
很多数据源都会为数据提供唯一的标识符,Dojo.data.Identify 接口则提供了基于唯一标识符进行数据获取定位的 API 支持。
{1}
- fetchItemByIdentity: function(/\*object\*/ keywordArgs) 同 Read 接口中的 fetch 方法类似,此方法也是一个异步方法,用户需要在参数对象中指定数据项获取后的回调处理函数。keywordArgs 参数是一个键值对对象,主要需要包括两个属性,一个是要进行指定获取的数据项标识符 identify,另一个则是回调处理函数 onItem。在指定 identify 的数据项获取成功后,onItem 回调函数则会被自动调用,以处理后续操作。

dataStore.fetchItemByIdentity({
// 指定要进行获取的数据项的 id
identity: “AS”,
// 设定数据返回后的回调函数
onItem: function(item){…},
// 设定错误回调函数
onError: function(error){…}
});

复制代码
- getIdentity: function(/\*item \*/ item) 此方法用于获取给定数据项的标识符。
{1}
## DataStore 应用
{1}
一般来说,Dijit 中的各个小部件都提供了对 DataStore 的支持,当我们在使用某个 Widget 来进行数据展现时,通常我们只需要根据数据源的格式类型来选择好 DataStore,然后在 Widget 声明中对 DataStore 进行指定就可以了。下面我们就通过 DataGridComboBox 作为数据展现 UI,基于不同的数据格式为它们设置不同的 DataStore
{1}
以下是一份 JSON 数据:
{1}
{1}

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”属性进行设置的。
Name Population Area

生成的 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 设计充分达成了数据展现层与数据管理层之间的松耦合,同时统一的数据访问接口使得对多种数据格式的应用以及程序移植都带来了相当大的便利性。

参考资料

2011-03-07 00:008374

评论

发布
暂无评论
发现更多内容

vivo 短视频推荐去重服务的设计实践

vivo互联网技术

服务器 架构设计 布隆过滤器

大数据培训-程序员职业生命周期短的原因

@零度

大数据开发

java培训-程序员怎样靠技术来延长职业周期

@零度

JAVA开发

TASKCTL调度服务平台节点管理

敏捷调度TASKCTL

数据仓库 kettle 元数据 ETL任务 任务调度

“逆行者”蒙牛 穿越疫情“暴风眼”

科技新消息

推出全新分布式计算接口!OneFlow v0.7.0发布,LiBai代码库、Serving、MLIR一应俱全

OneFlow

人工智能 机器学习 深度学习 深度学习框架 深度学习编译器

如何利用MHA+ProxySQL实现读写分离和负载均衡

华为云开发者联盟

MySQL 读写分离 高可用架构 ProxySQL MHA

一文解读GPU服务器!

Finovy Cloud

人工智能 GPU服务器 GPU算力

社交CRM系统解决方案

低代码小观

CRM 企业管理系统 社交软件 CRM系统 客户关系管理系统

自动搭建Maven私有仓库,不限容量、免费用

阿里云云效

maven 阿里云 云原生 Maven仓库 制品仓库

【课程汇总】OpenHarmony成长计划知识赋能第二期系列课程(附链接)

OpenHarmony开发者

OpenHarmony 成长计划

蒙牛“申”请出战 全力守“沪”战疫

科技新消息

OpenHarmony 3.1 Beta版本关键特性解析——ArkUI开发框架canvas组件绘制实践

OpenHarmony开发者

canvas OpenHarmony ArKUI 3.0

ModStartCMS Laravel9 v3.6.0内容标签增强,电脑手机适配

ModStart开源

JDK、Spring、Dubbo SPI 原理介绍

网易云信

Java spring

在 Kubernetes 中使用 Keycloak OIDC Provider 对用户进行身份验证

Se7en

Android技术分享| 安卓3行代码,实现整套音视频通话功能

anyRTC开发者

android 音视频 移动开发 实时通讯 视频通话

DapuStor大普微电子加入PolarDB开源数据库社区

阿里云数据库开源

数据库 阿里云 开源数据库 polarDB

中小企业知识管理的实施策略

小炮

企业知识管理

项目经理值得一试的思维方式:项目成功方程式

禅道项目管理

项目管理 软件开发

谈谈Java8-18引入的新特性

CRMEB

实例带你掌握如何分解条件表达式

华为云开发者联盟

代码 函数 条件表达式 条件分支 条件逻辑

围观报名中2022第十四届南京国际人工智能产品展会

InfoQ_caf7dbb9aa8a

守“沪”第一线 蒙牛使命必达

科技新消息

快来一起玩转LiteOS组件:Curl

华为云开发者联盟

LiteOS 文件传输 curl LiteOS组件 嵌入式设备

郑曌:从 ACM 世界冠军到技术 VP 的制胜之道

第四范式开发者社区

人工智能 数据库 编程 程序员 ACM

使用APICloud AVM框架封装app日历组件

YonBuilder低代码开发平台

前端开发 APP开发 APICloud 多端开发 avm.js

web前端培训-程序员失业后怎样才能再就业

@零度

web前端开发

MySQL 是怎样通讯的?

ooooooh灰灰

Go MySQL 协议 4月月更

eBPF Cilium实战(1) - 基于团队的网络隔离

北京好雨科技有限公司

Kubernetes PaaS rainbond

三高Mysql - Mysql索引和查询优化(偏实战部分)

懒时小窝

MySQL

Dojo Data Store——统一数据访问接口_Java_王强_InfoQ精选文章