基於HTML5的Web DataBase 可以讓你在瀏覽器中進行數據持久地存儲管理和有效查詢,假設你的離線應用程序有需要規范化的存儲功能,那么使用Web DataBase,可以使你的應用程序無論是在離線或者在線或者網絡不通暢情況下都可以將數據保存在客戶端。
下面是HTML5 DataBase中兩個不同的DataBase的比較,摘自http://www.html5rocks.com/en 上面的一篇文章。
我們這邊使用WebSQL來設計和編寫底層服務,W3C 的 WebDatabase 規范中說這份規范不再維護了,但是幾乎實現者都選擇了SQLite這種輕量簡單易用的客戶端數據庫:
現在我們來封裝和提取WebSQL公用方法。
首先,我們需要拿到SQLite數據庫可操作和執行的SQL數據上下文:
這邊通過openDatatBase方法打開或創建數據庫:

1 /*-------執行SQLite注入,數據庫的基本操作(Begin)-------*/ 2 function SQLProvider(dbName, size) { 3 this.dbName = dbName || 'OFLMAIL'; 4 5 var db = openDatabase(this.dbName, '1.0', 'database for ' + this.dbName, (size || 2) * 1024 * 1024); 6 this.db = db; 7 8 /*-------執行SQLite注入,數據庫的基本操作(End)-------*/ 9 10 function sqlerrorHandler(tx, e) { 11 log.error(e.message); 12 }
這邊還可以設置數據庫的名稱dbName和數據庫大小size,默認數據庫名稱是OFLMAIL,就是我們這個離線系統的名稱,默認大小是2兆。
我們還可以設置錯誤處理方法sqlErrorHandler,用戶處理操作失敗之后的錯誤捕捉
這樣,我們就拿到了操作SQLite的數據上下文db,通過上下文db,我們可以執行相應的CURD操作。
第一步,我們寫一個創建數據表的方法,把這個方法放在SQLProvider方法體里面,

1 /*--添加數據表--*/ 2 this.createTable = function (tableName, fields, callBack) { 3 var pkField = tableName + "_SEC"; 4 var sql = "CREATE TABLE IF NOT EXISTS " + tableName + "( " + pkField + " integer primary key autoincrement,"; 5 6 // 合並字段串同時去除傳入的主鍵字段 7 sql += fields.join(",").replace(pkField + ",", "") + ")"; 8 //log.debug(sql); 9 10 db.transaction(function (tx) { 11 tx.executeSql(sql, [], function () { 12 if (callBack) callBack(); 13 }, sqlerrorHandler); 14 }) 15 }
一共包含了三個參數tableName,fileds,callBack,分表代表你要創建的表名,所對應的字段數組,就是把這個表相應的字段用數組保存起來(方法里面還會自動創建一個表名加上“_SEC”的字段,他是個增量標識,用做主鍵),callBack顧名思義,回調函數,這個參數可以不傳。這個回調函數的存在很重要,因為整個基於SQLite數據庫的操作方法都是異步調用的,所以需要在回調函數中嵌套執行,否則有些執行會被中斷。
將這個函數放在SQLProvider里面,有一個好處就是到時候可以在SQLProvider的動態實例化中直接調用該函數
如:var sqlProvider = new SQLProvider();
sqlProvider.createTable(“UserInfo”,new Array(“UserName”,”UserPwd”));
這樣子,方便我們在頁面中調用。這種操作方法相當於C#里面的動態類創建方法,SQLProvider就是類名,createTable就是類中的方法,實例化調用。
接下來我們的數據庫的操作,包括數據表和數據的CURD操作,都會以這種方法寫在里面:
刪除數據表(只需傳入表名就行了,他會刪除相應的數據表):

1 /*--刪除數據表--*/ 2 this.dropTable = function (tableName) { 3 var sql = "DROP TABLE " + tableName; 4 db.transaction(function (tx) { 5 tx.executeSql(sql); 6 }) 7 }
添加數據(包含了四個參數:表名,字段數組,字段所對應的值的數組,和一個回調函數)
這邊的fields和values代表了字段數組和值數組,他們一一對應:
如 var fileds=new Array(“UserName”,”UserPwd”);
var values=new Array(“Ben”,”123456”);則說明在UserInfo表里面添加了一條數據,這條數據至少包含三個有值的字段,主鍵,UserName和UserPwd,而values 則是相應的值數組。
回調函數中帶有一個返回的參數,返回了你所添加的改行數據的主鍵。

1 /*--添加數據(插入數據)--*/ 2 this.insertRow = function (tableName, fields, values, callback) { 3 var sql = "INSERT INTO " + tableName + " (" + fields.join(",") + ") SELECT " 4 + new Array(values.length + 1).join(",?").substr(1); 5 6 db.transaction(function (tx) { 7 tx.executeSql(sql, values, function () { }, sqlerrorHandler); 8 //log.debug(sql); 9 10 tx.executeSql("SELECT max(" + tableName + "_SEC) id from " + tableName, [], function (tx, result) { 11 var item = result.rows.item(0); 12 var id = parseInt(item.id); 13 //log.debug("id=" + id); 14 if (callback) callback(id); 15 }, sqlerrorHandler); 16 }); 17 }
刪除數據 (包含了三個參數:表名tableName,主鍵sec和一個回調函數callback)
這個主鍵SEC是該待刪除的數據在Web DataBase中的主鍵,我們前面在建表的時候有一個增量標識字段,該字段的名稱為表名加上“_SEC”,因為唯一性,所以我們可以根據這個主鍵來刪除該行數據,
代碼如下:

1 /*--刪除數據--*/ 2 this.deleteRow = function (tableName, sec,callback) { 3 var pkField = tableName + "_SEC"; 4 var sql = "DELETE FROM " + tableName + " WHERE " + pkField + " = ?"; 5 db.transaction(function (tx) { 6 tx.executeSql(sql, [sec], null, sqlerrorHandler); 7 if (callback) callback(); //使用回調 8 }) 9 }
修改數據(這邊包括了四個參數,表名tableName,字段數組fields,值數組values,回調函數callback)
字段數組和值數組必須是一一對應的,而且第一個字段必須是主鍵,所對應的values的第一個值也必須是主鍵的值,這樣,可以根據字段的主鍵來查詢相應的數據行。
查出的數據行之后,可以根據后面的相應字段,進行修改。

1 /*--更新列,這邊需要注意的是兩個參數列表的首位必須是主鍵(或者說第一個必須是條件,后面的是修改位)--*/ 2 this.updateRow = function (tableName, fields, values,callback) { 3 var len = fields.length; 4 5 var sql = ""; 6 for (i = 1; i < len; i++) { 7 if (i == 1) sql += fields[i] + " = '" + values[i] + "'"; 8 else sql += "," + fields[i] + " = '" + values[i] + "'"; 9 } 10 11 sql = 'UPDATE ' + tableName + ' SET ' + sql + ' where ' + fields[0] + '= ?'; 12 //log.debug("sql:" + sql); 13 14 db.transaction(function (tx) { 15 tx.executeSql(sql, [values[0]], 16 null, sqlerrorHandler); 17 //log.debug("update " + tableName + " success! sec=" + values[0]); 18 if (callback) callback(); 19 }); 20 } 21 }
調用方式類似如下:
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),實際上結果集中也只有一條數據。

1 /*--讀取單行數據--*/ 2 this.readRow = function (tableName, sec, callback) { 3 db.transaction(function (tx) { 4 tx.executeSql('SELECT * FROM ' + tableName + ' WHERE ' + tableName + '_SEC = ?', [sec], function (tx, result) { 5 if (callback) callback(result.rows.item(0)); // 使用回調 6 }, sqlerrorHandler); 7 }); 8 }
讀取指定的數據表(根據表名來讀取相應的數據表,並返回結果集):

1 /*--讀取數據表--*/ 2 this.loadTable = function (tableName, callback) { 3 db.transaction(function (tx) { 4 tx.executeSql('SELECT * from ' + tableName, [], function (tx, result) { 5 if (callback) callback(result); //使用回調 6 }, sqlerrorHandler); 7 }); 8 }
結果集result中的列的集合用result.rows表示
列的數量用result.rows.length來表示
單條數據是用result.rows.item(index)表示,index指的是列的索引位置,從0開始
根據SQL的where條件語句來讀取指定的數據表(根據表名tableName和sqlSenten條件語句來執行,並返回結果集):

1 /*--根據查詢條件讀取數據表--*/ 2 this.loadTableBySQl = function (tableName, sqlSenten, callback) { 3 db.transaction(function (tx) { 4 tx.executeSql('SELECT * from ' + tableName+" WHERE "+ sqlSenten, [], function (tx, result) { 5 if (callback) callback(result); //使用回調 6 }, sqlerrorHandler); 7 }); 8 }
與上面的方法類似,只是多了一個sqlSenten條件語句來篩選數據
根據某個字段檢查是否存在該列(通過字段名和字段所對應的值)來進行操作,過多地用於根據主鍵來查詢數據行:

1 /*--檢查是否已存在該列--*/ 2 this.checkExist = function (tableName, fieldName, fieldValue, callback) { 3 db.transaction(function (tx) { 4 tx.executeSql('SELECT * from ' + tableName + ' where ' + fieldName + '= ?', [fieldValue], function (tx, result) { 5 var isExist; 6 if (result.rows.length == 1) isExist = "1"; else isExist = "0"; //1代表存在該行,0 代表不存在該行 7 if (callback) callback(isExist); 8 }, sqlerrorHandler); 9 }); 10 }
當檢索讀到的結果集合中包含了一條數據的時候,返回1,代表存在該行,為0的時候代表不存在該行。這邊做的其實不完善只能在唯一值的字段中才能夠過正確顯示,如主鍵,此外還可以通過where條件語句來驗證是否存在該行。這邊就不說了,自己去嘗試。
這樣就完成了整個離線數據庫的CURD操作,如果有不夠的地方,我們可以繼續修改完善,完整代碼如下,在代碼的結尾我們進行了實例化,我們把這些代碼獨立地存放到WebDataBase.js文件里面,這樣可以在繼承這個腳本文件的頁面里直接調用這個腳本庫的方法。
現在我們把這些數據庫的的操作應用到我們的系統中,
我們的用戶信息頁面(Information.html),用來保存登錄用戶的個人信息的:
包含了如下字段:姓名,性別,入職時間,工號和部門:
在載入的時候查看是否有數據,有數據則顯示第一條

1 $(document).ready(function () { 2 sqlProvider.loadTable("UserInfo", function (result) { 3 // result.rows 獲取到所有數據行 4 if (result.rows.length > 0) { 5 var row = result.rows.item(0); 6 $("#UserName").val(row.UserName); 7 $("#UserSex").val(row.UserSex); 8 $("#ReportDutyTime").val(row.ReportDutyTime); 9 $("#JobNumber").val(row.JobNumber); 10 $("#DepartmentNumber").val(row.DepartmentNumber); 11 //這邊包含一個隱藏域,可以保存該用戶信息的主鍵 12 $("#UserInfo_SEC").val(row.UserInfo_SEC); 13 } 14 }) 15 })
我們的保存按鈕的代碼如下:

1 function onformsumit() 2 { 3 //創建用戶信息表(存在跳過,不存在創建),包含六個字段, 4 //因為創建的時候會自動創建一個UserInfo_SEC的主鍵,所以實際上是6個字段 5 //UserName:用戶名稱 6 //UserSex:用戶性別 7 //ReportDutyTime:入職時間 8 //JobNumber:工號 9 //DepartmentNumber:部門 10 //Remark:備注 11 12 13 var UserName = $("#UserName").val(); 14 var UserSex = $("#UserSex").val(); 15 var ReportDutyTime = $("#ReportDutyTime").val(); 16 var JobNumber = $("#JobNumber").val(); 17 var DepartmentNumber = $("#DepartmentNumber").val(); 18 var Remark = ""; 19 20 var fields = new Array("UserName", "UserSex", "ReportDutyTime", "JobNumber", "DepartmentNumber", "Remark"); 21 var values = new Array(UserName,UserSex,ReportDutyTime,JobNumber,DepartmentNumber,Remark); 22 sqlProvider.createTable("UserInfo", fields, function () { 23 log.debug("創建數據表UserInfo"); 24 25 var UserInfo_SEC = $("#UserInfo_SEC").val(); 26 //取隱藏域的值,如果是0則為保存不為0則為修改 27 if (UserInfo_SEC == "0") { 28 sqlProvider.insertRow("UserInfo", fields, values, function () { 29 log.debug("插入數據成功!"); 30 alert("保存成功!"); 31 }); 32 } 33 else { 34 sqlProvider.updateRow("UserInfo", fields, values, function () { 35 log.debug("修改數據成功!"); 36 alert("保存成功!"); 37 }); 38 } 39 }); 40 return false; 41 }
保存成功之后,數據就存儲在我們離線的數據庫里面了,載入時顯示在頁面效果如下:
我們去瀏覽器中的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