基于 HTML5 中的 Web SQL Database 来构建应用程序

http://www.ibm.com/developerworks/cn/web/1108_zhaifeng_websqldb/



HTML5 Web SQL Database 简介

通过 Mark Pilgrim 的 Dive Into HTML5,我们了解到 HTML5 的很多新特性。但 HTML5 标准并不只局限于传统的标记语言,它还拥有很多让人期待的 API 接口,利用这些 API 接口,开发者可以创建更加丰富,更加引人入目的应用程序。比如支持文件拖放上传功能的 HTML5 File API。本文专注于 HTML5 的新特性:Web SQL Database API,使用本地和会话存储实现简单的对象持久化。

对于 Web 应用的存储,相信大家都接触过 Cookie。Cookie 用于弥补 HTTP 协议的无状态性,服务器可以使用 Cookie 中包含的信息来判断 HTTP 传输中的状态。但 Cookie 有自己固有的缺点:它的大小受限,大多数浏览器对 Cookie 大小限制为 4K;Cookie 机制可以在浏览器中被禁用;Cookie 需要在客户端和服务器端来回地传送,繁琐且消耗带宽;存在安全风险,Cookie 是以明文存放,可能被恶意客户修改,当然可以手动加密和解密 Cookie,但这需要额外的编码,并且因为加密和解密需要消耗一定的时间而影响应用程序的性能。

对于 HTML5,也许很有用的就是它新推出的“Web Storage”(Web 存储)API,它包括 localStorage 和 sessionStorage,对简单的键值对(比如应用程序设置)或简单对象(如应用程序状态)进行存储,使用本地和会话存储能够很好地完成,对于存储少量的数据非常有用,但是对大量的结构化数据进行处理时,它就力所不及了,而这正是 HTML5 的“Web SQL Database” API 接口的应用所在。

Web SQL Database API 实际上并不包含在 HTML5 规范之中。它是一个独立的规范,它引入了一套使用 SQL 操作客户端数据库的 API。最新版本的 Chrome,Safari 和 Opera 浏览器都支持 Web SQL Database。

Web SQL Database

在 W3C 的 Web SQL Database 规范中(参照 介绍)有这样的描述:Web SQL Database 引入了一套使用 SQL 来操纵客户端数据库(client-side database)的 API,这些 API 是异步的(asynchronous),所以作者在使用这套 API 时会发现匿名函数非常有用。规范中所使用的 SQL 语言为 SQLite 3.6.19。

其中 SQLite 是一款轻型的数据库,是遵循 ACID 的关系型数据库管理系统。它的设计目标是嵌入式的,它占用资源非常低,只需要几百 K 字节的内存就可以了。它能够支持 Windows/Linux/Unix 等主流操作系统,同时能够跟很多程序语言相结合,如 C#,PHP,Java,JavaScript 等,还有 ODBC 接口,比起 Mysql,PostgreSQL 这两款开源的数据库管理系统来说,它的处理速度更快。

本文将介绍 Web SQL Database 规范中定义的三个核心方法:

  1. openDatabase:这个方法使用现有数据库或新建数据库来创建数据库对象
  2. transaction:这个方法允许我们根据情况控制事务提交或回滚
  3. executeSql:这个方法用于执行真实的 SQL 查询

注意:对于下面的内容需要读者对 JavaScript 和面向对象编程(特别是匿名内部类的内部函数)以及 SQL 具有很好的理解。

HTML5 Web SQL Database API

1.Database

每个域都有一组相关的数据库,每个数据库有名字和当前的版本号。这套 API 并不提供遍历或删除域中的某个数据库的功能。每个数据库一时间只能有一个版本号,不能一时间拥有多个版本号,版本号用来保护数据库不被写入脏数据。

清单 1.Database API
 [Supplemental, NoInterfaceObject] 
 interface WindowDatabase { 
 Database openDatabase(in DOMString name, in DOMString version, in DOMString displayName,
  in unsigned long estimatedSize, in optional DatabaseCallback creationCallback);
 }; 
 Window implements WindowDatabase; 

 [Supplemental, NoInterfaceObject] 
 interface WorkerUtilsDatabase { 
 Database openDatabase(in DOMString name, in DOMString version, in DOMString displayName,
  in unsigned long estimatedSize, in optional DatabaseCallback creationCallback);
 DatabaseSync openDatabaseSync(in DOMString name, in DOMString version,
  in DOMString displayName, in unsigned long estimatedSize,
  in optional DatabaseCallback creationCallback);
 }; 
 WorkerUtils implements WorkerUtilsDatabase; 

 [Callback=FunctionOnly, NoInterfaceObject] 
 interface DatabaseCallback { 
  void handleEvent(in Database database);
 };

接口 Window、WorkerUtils 的 openDatabase() 方法和接口 WorkerUtils 的 openDatabaseSync() 方法接受以下参数:一个数据库名字(name),一个数据库版本号(version),一个显示名字(displayName),数据库将要保存数据的大小(estimatedSize,以字节为单位 ),一个可选的回调函数(createionCallback,如果数据库没有被创建,这个函数将会被调用 )。如果提供了回调函数,回调函数用以调用 changeVersion() 函数,不管给定什么样的版本号,回调函数将把数据库的版本号设置为空;如果没有提供回调函数,则以给定的版本号创建数据库。

包括空字符串在内的所有字符串都可以作为有效地数据库名称,数据库名称区分大小写,且可以比较。

2.异步数据库 API(Asynchronous Database API)

清单 2. 异步数据库 API
 interface Database { 
  void transaction(in SQLTransactionCallback callback, 
  in optional SQLTransactionErrorCallback errorCallback, 
  in optional SQLVoidCallback successCallback);
  void readTransaction(in SQLTransactionCallback callback, 
  in optional SQLTransactionErrorCallback errorCallback, 
  in optional SQLVoidCallback successCallback);
  readonly attribute DOMString version;
  void changeVersion(in DOMString oldVersion, 
  in DOMString newVersion, in optional SQLTransactionCallback callback,
   in optional SQLTransactionErrorCallback errorCallback,
    in optional SQLVoidCallback successCallback);
 }; 

 [Callback=FunctionOnly, NoInterfaceObject] 
 interface SQLVoidCallback { 
  void handleEvent();
 }; 

 [Callback=FunctionOnly, NoInterfaceObject] 
 interface SQLTransactionCallback { 
  void handleEvent(in SQLTransaction transaction);
 }; 

 [Callback=FunctionOnly, NoInterfaceObject] 
 interface SQLTransactionErrorCallback { 
  void handleEvent(in SQLError error);
 };

方法 transaction() 和 readTransaction() 有一个到三个参数,后两个参数为可选的,分别表示错误回调函数和成功回调函数。transaction() 方法为 read/write 模式,readTransaction() 方法为只读模式;获取属性 version 必须返回数据库的当前版本号;方法 changeVersion 允许脚本自动地检查版本号,并修改它。

2.1 执行 SQL 语句

清单 3. 执行 SQL 语句 API
 typedef sequence<any> ObjectArray; 

 interface SQLTransaction { 
  void executeSql(in DOMString sqlStatement, in optional ObjectArray arguments,
   in optional SQLStatementCallback callback,
    in optional SQLStatementErrorCallback errorCallback);
 }; 

 [Callback=FunctionOnly, NoInterfaceObject] 
 interface SQLStatementCallback { 
  void handleEvent(in SQLTransaction transaction, in SQLResultSet resultSet);
 }; 

 [Callback=FunctionOnly, NoInterfaceObject] 
 interface SQLStatementErrorCallback { 
  boolean handleEvent(in SQLTransaction transaction, in SQLError error);
 };

这个函数具有四个参数:表示查询的字符串(sqlStatement);插入到查询语句中问号所在处的字符串数据(arguments);一个可选的成功时执行函数(callback);一个可选的失败时执行函数(errorCallback)。

3.数据库查询结果(Database Query Result)

清单 4. 查询结果集 API
 interface SQLResultSet { 
readonly attribute long insertId;
readonly attribute long rowsAffected;
readonly attribute SQLResultSetRowList rows;
 };

如果插入数据库一行数据,insertId 代表这个行号;如果插入多行数据,insertId 代表插入数据的最后一行行号。

SQL 语句执行后改变的行数用 rowsAffected 表示,如果 SQL 语句没有改变任何行,则 rowsAffected 为 0,对于“SELECT”语句,rowsAffected 就为 0.

rows 为一个 SQLResultSetRowList 对象,代表数据库按顺序返回的行。如果没有返回任何行,则这个对象为空。

一个入门的例子详解

本例将完整地演示 Web SQL Database API 的使用,建立数据库、建立表格、插入数据、查询数据、将查询结果显示。这个例子只能在最新版本的 Chrome、Safari 或 Opera 浏览器中产生输出结果。

清单 5. 例子源码
1. <!DOCTYPE HTML>
2. <html>
3. <head>
4. <script type="text/javascript">
5. var db = openDatabase('mydb', '1.0', 'Test DB', 2 * 1024 * 1024);
6. var msg;
7. db.transaction(function (tx) {
8.    tx.executeSql('CREATE TABLE IF NOT EXISTS LOGS (id unique, log)');
9.    tx.executeSql('INSERT INTO LOGS (id, log) VALUES (1, "foobar")');
10.    tx.executeSql('INSERT INTO LOGS (id, log) VALUES (2, "logmsg")');
11.    msg = '<p>Log message created and row inserted.</p>';
12.    document.querySelector('#status').innerHTML =   msg;
13. });
14.
15. db.transaction(function (tx) {
16.    tx.executeSql('SELECT * FROM LOGS', [], function (tx, results) {
17.    var len = results.rows.length, i;
18.     msg = "<p>Found rows: " + len + "</p>";
19.     document.querySelector('#status').innerHTML +=   msg;
20.    for (i = 0; i < len; i++){
21.       msg = "<p><b>" + results.rows.item(i).log + "</b></p>";
22.       document.querySelector('#status').innerHTML +=   msg;
23.     }
24. }, null);
25. });
26. </script>
27. </head>
28. <body>
29. <div id="status" name="status">Status Message</div>
30. </body>
31. </html>

第五行的 var db = openDatabase('mydb', '1.0', 'Test DB', 2 * 1024 * 1024);

建立一个名称为 mydb 的数据库,它的版本为 1.0,描述信息为 Test DB,大小为 2M 字节。openDatabase 方法打开一个已经存在的数据库,如果数据库不存在则创建数据库,创建数据库的语法如下:

Database openDatabase(in DOMString name, in DOMString version, in DOMString displayName, in unsigned long estimatedSize, in optional DatabaseCallback creationCallback)

方法用到五个参数:

  1. 数据库名
  2. 版本号
  3. 描述
  4. 数据库大小
  5. 创建回调函数

最后一个参数创建回调函数,在创建数据库的时候调用,但即使没有这个参数,一样可以运行时创建数据库。

执行创建数据库后在 Chrome 中可以看到如下情形:

图 1. 创建数据库 mydb
图 1. 创建数据库 mydb

可以看到此时有数据库建立,但并无表格建立。

第七行到第十三行:

7. db.transaction(function (tx) {
8.    tx.executeSql('CREATE TABLE IF NOT EXISTS LOGS (id unique, log)');
9.    tx.executeSql('INSERT INTO LOGS (id, log) VALUES (1, "foobar")');
10.    tx.executeSql('INSERT INTO LOGS (id, log) VALUES (2, "logmsg")');
11.    msg = '<p>Log message created and row inserted.</p>';
12.    document.querySelector('#status').innerHTML =   msg;
13. });

通过第八行语句可以在 mydb 数据库中建立一个 LOGS 表格。在这里只执行创建表格语句,而不执行后面两个插入操作时,将在 Chrome 中看到如下的情形:

图 2. 在数据库 mydb 中建立表格 LOGS
图 2. 在数据库 mydb 中建立表格 LOGS

可以看到在数据库 mydb 中有表格 LOGS 建立,但表格 LOGS 为空。

九,十两行执行插入操作,在插入新记录时,我们还可以传递动态值,如:

 var db = openDatabase('mydb', '1.0', 'Test DB', 2 * 1024 * 1024);
 db.transaction(function (tx) { 
    tx.executeSql('CREATE TABLE IF NOT EXISTS LOGS (id unique, log)');
    tx.executeSql('INSERT INTO LOGS (id,log) VALUES (?, ?'), [e_id, e_log];
 });

这里的 e_id 和 e_log 为外部变量,executeSql 在数组参数中将每个变量映射到“?”。

在插入操作执行后,可以在 Chrome 中看到数据库的状态:

图 3. 在数据库 mydb 的表格 LOGS 中插入数据
图 3. 在数据库 mydb 的表格 LOGS 中插入数据

可以看到插入的数据,此时并未执行查询语句,可以看到页面中的显示如下,并没有出现查询结果:

图 4. 插入数据后的页面
图 4. 插入数据后的页面

如果要读取已经存在的记录,我们使用一个回调函数捕获结果,如上面的第十五到第二十五行代码:

15. db.transaction(function (tx) {
16.    tx.executeSql('SELECT * FROM LOGS', [], function (tx, results) {
17.    var len = results.rows.length, i;
18.     msg = "<p>Found rows: " + len + "</p>";
19.     document.querySelector('#status').innerHTML +=   msg;
20.    for (i = 0; i < len; i++){
21.       msg = "<p><b>" + results.rows.item(i).log + "</b></p>";
22.       document.querySelector('#status').innerHTML +=   msg;
23.     }
24. }, null);
25. });

执行查询之后,将信息输出到页面中,可以看到页面中状态如下:

图 5. 执行查询后的页面
图 5. 执行查询后的页面

结束语

本文介绍了 HTML5 的 Web SQL Database 特点,对其 API 进行介绍。需要注意的是,如果不是绝对需要,不要使用 Web SQL Database,因为它会让我们的代码更加复杂(匿名内部类的内部函数,回调函数等等)。对大多数情况下,本地存储或会话存储就能够完成相应的任务,尤其是你能够保持对象状态持久化的情况。通过这些 HTML5 Web SQL Database API 接口,可以获得更多功能,相信以后会出现一些非常优秀的、建立在这些 API 之上的应用程序。

参考资料

学习

讨论

  • 加入 developerWorks 中文社区。查看开发人员推动的博客、论坛、组和维基,并与其他 developerWorks 用户交流。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值