基于HTML5的Web DataBase 可以让你在浏览器中进行数据持久地存储管理和有效查询,假设你的离线应用程序有需要规范化的存储功能,那么使用Web DataBase,可以使你的应用程序无论是在离线或者在线或者网络不通畅情况下都可以将数据保存在客户端。

下面是HTML5 DataBase中两个不同的DataBase的比较,摘自http://www.html5rocks.com/en 上面的一篇文章。

我们这边使用WebSQL来设计和编写底层服务,W3C 的 WebDatabase 规范中说这份规范不再维护了,但是几乎实现者都选择了SQLite这种轻量简单易用的客户端数据库:

现在我们来封装和提取WebSQL公用方法。

首先,我们需要拿到SQLite数据库可操作和执行的SQL数据上下文:

这边通过openDatatBase方法打开或创建数据库:

/*-------执行SQLite注入,数据库的基本操作(Begin)-------*/
function SQLProvider(dbName, size) {
this.dbName = dbName || 'OFLMAIL'; var db = openDatabase(this.dbName, '1.0', 'database for ' + this.dbName, (size || 2) * 1024 * 1024);
this.db = db; /*-------执行SQLite注入,数据库的基本操作(End)-------*/ function sqlerrorHandler(tx, e) {
log.error(e.message);
}

这边还可以设置数据库的名称dbName和数据库大小size,默认数据库名称是OFLMAIL,就是我们这个离线系统的名称,默认大小是2兆。

我们还可以设置错误处理方法sqlErrorHandler,用户处理操作失败之后的错误捕捉

这样,我们就拿到了操作SQLite的数据上下文db,通过上下文db,我们可以执行相应的CURD操作。

第一步,我们写一个创建数据表的方法,把这个方法放在SQLProvider方法体里面,

/*--添加数据表--*/
this.createTable = function (tableName, fields, callBack) {
var pkField = tableName + "_SEC";
var sql = "CREATE TABLE IF NOT EXISTS " + tableName + "( " + pkField + " integer primary key autoincrement,"; // 合并字段串同时去除传入的主键字段
sql += fields.join(",").replace(pkField + ",", "") + ")";
//log.debug(sql); db.transaction(function (tx) {
tx.executeSql(sql, [], function () {
if (callBack) callBack();
}, sqlerrorHandler);
})
}

一共包含了三个参数tableName,fileds,callBack,分表代表你要创建的表名,所对应的字段数组,就是把这个表相应的字段用数组保存起来(方法里面还会自动创建一个表名加上“_SEC”的字段,他是个增量标识,用做主键),callBack顾名思义,回调函数,这个参数可以不传。这个回调函数的存在很重要,因为整个基于SQLite数据库的操作方法都是异步调用的,所以需要在回调函数中嵌套执行,否则有些执行会被中断。

将这个函数放在SQLProvider里面,有一个好处就是到时候可以在SQLProvider的动态实例化中直接调用该函数

如:var sqlProvider = new SQLProvider();

sqlProvider.createTable(“UserInfo”,new Array(“UserName”,”UserPwd”));

这样子,方便我们在页面中调用。这种操作方法相当于C#里面的动态类创建方法,SQLProvider就是类名,createTable就是类中的方法,实例化调用。

接下来我们的数据库的操作,包括数据表和数据的CURD操作,都会以这种方法写在里面:

删除数据表(只需传入表名就行了,他会删除相应的数据表):

/*--删除数据表--*/
this.dropTable = function (tableName) {
var sql = "DROP TABLE " + tableName;
db.transaction(function (tx) {
tx.executeSql(sql);
})
}

添加数据(包含了四个参数:表名,字段数组,字段所对应的值的数组,和一个回调函数)

这边的fields和values代表了字段数组和值数组,他们一一对应:

如 var fileds=new Array(“UserName”,”UserPwd”);

var values=new Array(“Ben”,”123456”);则说明在UserInfo表里面添加了一条数据,这条数据至少包含三个有值的字段,主键,UserName和UserPwd,而values 则是相应的值数组。

回调函数中带有一个返回的参数,返回了你所添加的改行数据的主键。

/*--添加数据(插入数据)--*/
this.insertRow = function (tableName, fields, values, callback) {
var sql = "INSERT INTO " + tableName + " (" + fields.join(",") + ") SELECT "
+ new Array(values.length + 1).join(",?").substr(1); db.transaction(function (tx) {
tx.executeSql(sql, values, function () { }, sqlerrorHandler);
//log.debug(sql); tx.executeSql("SELECT max(" + tableName + "_SEC) id from " + tableName, [], function (tx, result) {
var item = result.rows.item(0);
var id = parseInt(item.id);
//log.debug("id=" + id);
if (callback) callback(id);
}, sqlerrorHandler);
});
}

删除数据 (包含了三个参数:表名tableName,主键sec和一个回调函数callback)

这个主键SEC是该待删除的数据在Web DataBase中的主键,我们前面在建表的时候有一个增量标识字段,该字段的名称为表名加上“_SEC”,因为唯一性,所以我们可以根据这个主键来删除该行数据,

代码如下:

/*--删除数据--*/
this.deleteRow = function (tableName, sec,callback) {
var pkField = tableName + "_SEC";
var sql = "DELETE FROM " + tableName + " WHERE " + pkField + " = ?";
db.transaction(function (tx) {
tx.executeSql(sql, [sec], null, sqlerrorHandler);
if (callback) callback(); //使用回调
})
}

修改数据(这边包括了四个参数,表名tableName,字段数组fields,值数组values,回调函数callback)

字段数组和值数组必须是一一对应的,而且第一个字段必须是主键,所对应的values的第一个值也必须是主键的值,这样,可以根据字段的主键来查询相应的数据行。

查出的数据行之后,可以根据后面的相应字段,进行修改。

/*--更新列,这边需要注意的是两个参数列表的首位必须是主键(或者说第一个必须是条件,后面的是修改位)--*/
this.updateRow = function (tableName, fields, values,callback) {
var len = fields.length; var sql = "";
for (i = 1; i < len; i++) {
if (i == 1) sql += fields[i] + " = '" + values[i] + "'";
else sql += "," + fields[i] + " = '" + values[i] + "'";
} sql = 'UPDATE ' + tableName + ' SET ' + sql + ' where ' + fields[0] + '= ?';
//log.debug("sql:" + sql); db.transaction(function (tx) {
tx.executeSql(sql, [values[0]],
null, sqlerrorHandler);
//log.debug("update " + tableName + " success! sec=" + values[0]);
if (callback) callback();
});
}
}

调用方式类似如下:

var fileds=new Array(“UserInfo_SEC”,“UserName”,”UserPwd”);

var values=new Array(“5”,“Ben”,”123456”);

sqlProvider.updateRow(“UserInfo”,fileds ,values,function(){

log.debug(“修改成功!”);

});

这样子就是在UserInfo表里面修改主键为5的数据行,修改它的UserName的值为:“Ben”,

修改它的UserPwd的值为:“123456”

根据主键查询单行数据(包含三个参数表名tableName,主键SEC,回调函数callback):

根据表名和主键名称获取到该行数据,并返回,注意到这边通过cllback回调函数来返回查询的结果,通过数据上下文tx执行该SQL脚本,返回的是结果集result,这边我们取他结果集的第一条数据也即是result.rows.item(0),实际上结果集中也只有一条数据。

/*--读取单行数据--*/
this.readRow = function (tableName, sec, callback) {
db.transaction(function (tx) {
tx.executeSql('SELECT * FROM ' + tableName + ' WHERE ' + tableName + '_SEC = ?', [sec], function (tx, result) {
if (callback) callback(result.rows.item(0)); // 使用回调
}, sqlerrorHandler);
});
}

读取指定的数据表(根据表名来读取相应的数据表,并返回结果集):

/*--读取数据表--*/
this.loadTable = function (tableName, callback) {
db.transaction(function (tx) {
tx.executeSql('SELECT * from ' + tableName, [], function (tx, result) {
if (callback) callback(result); //使用回调
}, sqlerrorHandler);
});
}

结果集result中的列的集合用result.rows表示

列的数量用result.rows.length来表示

单条数据是用result.rows.item(index)表示,index指的是列的索引位置,从0开始

根据SQL的where条件语句来读取指定的数据表(根据表名tableName和sqlSenten条件语句来执行,并返回结果集):

/*--根据查询条件读取数据表--*/
this.loadTableBySQl = function (tableName, sqlSenten, callback) {
db.transaction(function (tx) {
tx.executeSql('SELECT * from ' + tableName+" WHERE "+ sqlSenten, [], function (tx, result) {
if (callback) callback(result); //使用回调
}, sqlerrorHandler);
});
}

与上面的方法类似,只是多了一个sqlSenten条件语句来筛选数据

根据某个字段检查是否存在该列(通过字段名和字段所对应的值)来进行操作,过多地用于根据主键来查询数据行:

/*--检查是否已存在该列--*/
this.checkExist = function (tableName, fieldName, fieldValue, callback) {
db.transaction(function (tx) {
tx.executeSql('SELECT * from ' + tableName + ' where ' + fieldName + '= ?', [fieldValue], function (tx, result) {
var isExist;
if (result.rows.length == 1) isExist = "1"; else isExist = "0"; //1代表存在该行,0 代表不存在该行
if (callback) callback(isExist);
}, sqlerrorHandler);
});
}

当检索读到的结果集合中包含了一条数据的时候,返回1,代表存在该行,为0的时候代表不存在该行。这边做的其实不完善只能在唯一值的字段中才能够过正确显示,如主键,此外还可以通过where条件语句来验证是否存在该行。这边就不说了,自己去尝试。

这样就完成了整个离线数据库的CURD操作,如果有不够的地方,我们可以继续修改完善,完整代码如下,在代码的结尾我们进行了实例化,我们把这些代码独立地存放到WebDataBase.js文件里面,这样可以在继承这个脚本文件的页面里直接调用这个脚本库的方法。

现在我们把这些数据库的的操作应用到我们的系统中,

我们的用户信息页面(Information.html),用来保存登录用户的个人信息的:

包含了如下字段:姓名,性别,入职时间,工号和部门:

在载入的时候查看是否有数据,有数据则显示第一条

$(document).ready(function () {
sqlProvider.loadTable("UserInfo", function (result) {
// result.rows 获取到所有数据行
if (result.rows.length > 0) {
var row = result.rows.item(0);
$("#UserName").val(row.UserName);
$("#UserSex").val(row.UserSex);
$("#ReportDutyTime").val(row.ReportDutyTime);
$("#JobNumber").val(row.JobNumber);
$("#DepartmentNumber").val(row.DepartmentNumber);
//这边包含一个隐藏域,可以保存该用户信息的主键
$("#UserInfo_SEC").val(row.UserInfo_SEC);
}
})
})

我们的保存按钮的代码如下:

function onformsumit()
{
//创建用户信息表(存在跳过,不存在创建),包含六个字段,
//因为创建的时候会自动创建一个UserInfo_SEC的主键,所以实际上是6个字段
//UserName:用户名称
//UserSex:用户性别
//ReportDutyTime:入职时间
//JobNumber:工号
//DepartmentNumber:部门
//Remark:备注 var UserName = $("#UserName").val();
var UserSex = $("#UserSex").val();
var ReportDutyTime = $("#ReportDutyTime").val();
var JobNumber = $("#JobNumber").val();
var DepartmentNumber = $("#DepartmentNumber").val();
var Remark = ""; var fields = new Array("UserName", "UserSex", "ReportDutyTime", "JobNumber", "DepartmentNumber", "Remark");
var values = new Array(UserName,UserSex,ReportDutyTime,JobNumber,DepartmentNumber,Remark);
sqlProvider.createTable("UserInfo", fields, function () {
log.debug("创建数据表UserInfo"); var UserInfo_SEC = $("#UserInfo_SEC").val();
//取隐藏域的值,如果是0则为保存不为0则为修改
if (UserInfo_SEC == "0") {
sqlProvider.insertRow("UserInfo", fields, values, function () {
log.debug("插入数据成功!");
alert("保存成功!");
});
}
else {
sqlProvider.updateRow("UserInfo", fields, values, function () {
log.debug("修改数据成功!");
alert("保存成功!");
});
}
});
return false;
}

保存成功之后,数据就存储在我们离线的数据库里面了,载入时显示在页面效果如下:

我们去浏览器中的DataBase中查看,就可以看到这条数据了,如图:

//以下是表单重置函数,删掉该用户信息的代码

function resets() {

var UserInfo_SEC = $("#UserInfo_SEC").val();

if (UserInfo_SEC != "0") {

sqlProvider.deleteRow("UserInfo", UserInfo_SEC, function () {

window.location.reload(true);

})

}

}

2010.11.18, W3C 宣布 将不再关注Web SQL databas,并且不再维护它的过时的规范,浏览器厂商也不会再在他们的新版浏览器中更新和升级这一块,取而代之的就是IndexedDB,W3C组织鼓励和推崇使用IndexedDB。所以,建议学习人员去看一下IndexedDB的使用方法。

这是相关材料:http://www.html5rocks.com/en/tutorials/webdatabase/websql-indexeddb/

本文的源码:CRX_Mail_WebDataBase

使用HTML5 WebDataBase设计离线数据库的更多相关文章

  1. HTML5项目笔记10:使用HTML5 IndexDB设计离线数据库

    之前的文章(http://www.cnblogs.com/wzh2010/archive/2012/05/22/2514017.html)里面描述了HTML5 离线数据存储的Web SQL,一个基于S ...

  2. html5 webDatabase 存储中sql语句执行可嵌套使用

    html5 webDatabase 存储中sql语句执行可嵌套使用,代码如下: *); data.transaction(function(tx){ tx.executeSql("creat ...

  3. 25个让人无法抗拒的HTML5网站设计实例

    原文地址:http://www.goodfav.com/html5-website-designs-8272.html HTML5在其功能方面给网络市场带来了惊人的改进. HTML5是万维网联盟,在起 ...

  4. 物理数据模型(PDM)->概念数据模型 (CDM)->面向对象模型 (OOM):适用于已经设计好数据库表结构了。

    物理数据模型(PDM)->概念数据模型 (CDM)->面向对象模型 (OOM):适用于已经设计好数据库表结构了.   步骤如下: 一.反向生成物理数据模型PDM 开发环境 PowerDes ...

  5. Python 基于python+mysql浅谈redis缓存设计与数据库关联数据处理

    基于python+mysql浅谈redis缓存设计与数据库关联数据处理 by:授客  QQ:1033553122 测试环境 redis-3.0.7 CentOS 6.5-x86_64 python 3 ...

  6. 微信HTML5页面设计建议

    一个HTML5页面从提出到完成上线的流程:>   1.需求方.设计人员.H5实现人员三方共同讨论实现方案 2.设计人员出设计图 3.H5人员按设计图出H5页面 4.需求方评估已实现的H5页面后给 ...

  7. 写给“有钱大爷”、”美工殿下”、“前端文艺青年”的微信HTML5页面设计建议

    ==============================   2018更新 iphone X 的设计内容   ==============================     我保证你一分钟就 ...

  8. 设计WEB数据库(学习)

    设计WEB数据库 1.考虑建模的实际对象 为现实世界的实体和关系建立模型 在上面情况下考虑建表呢? 答:如果有一组属于同一类型的数据,就可以根据这些数据创建表 2.避免保存冗余数据 原因:a.空间的浪 ...

  9. HTML5的设计目的是为了在移动设备上支持多媒体

    HTML5的设计目的是为了在移动设备上支持多媒体

随机推荐

  1. vsftpd.conf 联机手册

    vsftpd.conf - vsftpd 的配置文件 描述vsftpd.conf 可以用于控制 vsftpd, 以实现各种各样的功能. vsftpd 缺省到 /etc/vsftpd.conf 处查找此 ...

  2. iscc2016-好长的字符串

    Vm0wd2QyVkhVWGhVYmxKV1YwZDRXRmxVUm5kVlJscHpXa2M1VjFKdGVGWlZNbmhQWVd4YWMxZHViRmROYWxaeVdWZDRZV014WkhG ...

  3. CSS HACK的方法

    所有浏览器 通用 height: 100px; IE6 专用 _height: 100px; IE7 专用 *+height: 100px; IE6.IE7 共用 *height: 100px; IE ...

  4. Instagram的技术架构

    http://blogread.cn/it/article/5497 Instagram 被 Facebook 以10亿美金收购.团队规模:13 人.而在被Facebook收购前的一个月,整个团队才7 ...

  5. Flip Game

    http://poj.org/problem?id=1753 #include<cstdio> #include<algorithm> #include<string.h ...

  6. 应用程序的关闭退出(在FMX中,Activity替代了Form的概念)

    在VCL中,关闭程序的主窗体也就意味着程序的主循环结束,主程序自然而然结束.所以在主窗体中使用窗体的关闭函数(Close)即可,如下: procedure TfrmMain.btncloseClick ...

  7. C 函数原型

    int add(int,int);//add two int numbers and return it--- add function prototype; int main(int argc, c ...

  8. kernel解读之 pick_next_rt_entity

    1328 static struct sched_rt_entity *pick_next_rt_entity(struct rq *rq, 1329 struct rt_rq *rt_rq) 133 ...

  9. BZOJ 1053 [HAOI2007]反素数ant

    1053: [HAOI2007]反素数ant Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 1948  Solved: 1094[Submit][St ...

  10. CF-164C. Machine Programming(最小费用最大流)

    题意: 给你n个任务,k个机器,n个任务的起始时间,持续时间,完成任务的获利 每个机器可以完成任何一项任务,但是同一时刻只能完成一项任务,一旦某台机器在完成某项任务时,直到任务结束,这台机器都不能去做 ...