HTML5 Web SQL Database 對象本地持久化
HTML 5标準并不隻局限于傳統的标記語言,它還擁有很多讓人期待的API接口,利用這些接口,開發者可以建立更加豐富、更加引人注目的應用程式。之前我們介紹過支援檔案拖放上傳功能的HTML 5 File API,今天,我們一起來了解HTML 5的Web SQL Database API,使用本地和會話存儲實作簡單的對象持久化。
對于HTML 5,也許最為有用的就是它新推出的“Web Storage”(Web 存儲)API。對簡單的關鍵值對(比如應用程式設定)或簡單對象(如應用程式狀态)進行存儲,使用本地和會話存儲能夠很好地完成,但是在對瑣碎的關系資料進行處理之外,它就力所不及了。而這正是 HTML 5 的“Web SQL Database”API 接口的應用所在。
近日Google宣布将支援HTML 5 Web SQL Database API,其他浏覽器廠商也表示将緊随其後提供該支援,有的甚至已經開始支援該API了;但同時,HTML 5規範的制訂卻遇到了阻礙,因為所有的參與者都已選擇了SQLite作為底層資料庫,要想實作标準化還得考慮多個不同的實作。
作為HTML 5的一部分,W3C組織正在制訂Web SQL Database API草案,該規範主要用于解決如何通過SQL存儲及通路資料的問題。文檔中所使用的SQL語言是SQLite 3.6.19。網頁可以使用這個API與嵌入式的用戶端資料庫進行互動,這對于那些想要在本地存儲資料或是離線浏覽的應用來說價值非常大。
Google已經在其最新的浏覽器Chrome 4中通過SQLite提供對Web SQL Database的支援了,這個舉動可以看作是向标準化邁進的一大步,因為Google Gears中已經擁有了一個Database API,也是基于SQLite。Gears API為所有主流浏覽器提供了結構化的資料存儲功能,包括IE、Firefox以及Safari,但現在Google已經停止Gears的開發工作了。
Firefox 3擁有一個嵌入式SQLite資料庫,目前主要用于存儲書簽和曆史記錄,但可能不久後就将支援Web SQL Database API。目前的開發工作正在WebKit(Safari所用的渲染引擎)上進行以向Web開發者提供Web Database API。現在誰也不知道微軟對于IE和HTML 5 Database API的計劃到底是什麼。
先提個醒,該文下面的内容需要讀者對 JavaScript 和面對對象程式設計(尤其是匿名内的内部函數)以及SQL具有很好的了解。
打開連結
為了打開一個連接配接,我們執行以下代碼:
db = openDatabase("ToDo", "0.1", "A list of to do items.", 200000);
以上代碼建立了一個資料庫對象 db,名稱是 Todo,版本編号為0.1。db 還帶有描述資訊和大概的大小值。使用者代理(user agent)可使用這個描述與使用者進行交流,說明資料庫是用來做什麼的。利用代碼中提供的大小值,使用者代理可以為内容留出足夠的存儲。如果需要,這個大小是可以改變的,是以沒有必要預先假設允許使用者使用多少空間。
為了檢測之前建立的連接配接是否成功,你可以檢查那個資料庫對象是否為null:
if(!db) alert("Failed to connect to database.");
絕不可以假設該連接配接已經成功建立,即使過去對于某個使用者它是成功的。為什麼一個連接配接會失敗,存在多個原因。也許使用者代理出于安全原因拒絕你的通路,也許裝置存儲有限。面對活躍而快速進化的潛在使用者代理,對使用者的機器、軟體及其能力作出假設是非常不明智的行為。比如,當使用者使用手持裝置時,他們可自由處置的資料可能隻有幾兆位元組。
執行查詢
執行一個查詢,你可以使用database.transaction()函數。該函數具有單一參數,負責查詢實際執行的函數。
該函數(通常是匿名的)具有一個類型事務的參數。
db.transaction( function(tx) { 該事務具有一個函數:executeSql。這個函數使用四個參數:表示查詢的字元串,插入到查詢中問号所在處的字元串資料(很像 Java 的預先準備好的語句),一個成功時執行的函數和一個失敗時執行的函數。
tx.executeSql("SELECT COUNT(*) FROM ToDo", [], function(result){}, function(tx, error){});
查詢成功時
當查詢成功執行時,應用程式跳轉至一個具有一對參數的查詢,一個是 transaction,另一個是它搜集的 results。對于實際上将你的資料傳遞至使用者,這是非常完美的,比如顯示 ToDo 清單。有關這個話題後面再講。
查詢失敗失敗時
當查詢沒能執行時執行。由于你将 transaction 對象作為函數的第一個參數進行傳遞,當出現錯誤時你可以繼續執行查詢。例如,如果是因為缺少表格(table)而查詢無法運作,這是建立一個表格并在此執行該語句的絕佳時機。從該函數的第二個參數,你可以獲得有關該錯誤的資訊(包括描述)。
示例
假設我們想要使用上面的例子,想要查詢資料庫中的某個表格,如果該表格不存在,我們就建立一個表格。
在這個示例中,我們将調用具有一個函數參數的 db.transaction()。這個參數中,我們調用 tx.executeSql()。如果這個步驟成功,我們不做任何操作(是以是一個null參數)。或者我們将該事務和執行失敗的函數一起傳遞,并再次調用 tx.executeSql()。這一次使用建立查詢。
db.transaction( function(tx) { tx.executeSql("SELECT COUNT(*) FROM ToDo", [], null, function(tx, error) { tx.executeSql("CREATE TABLE ToDo (id REAL UNIQUE, label TEXT, timestamp REAL)", [], null, null); } ); } );
使用所有這些内部方法,可能有點麻煩,是以你也許想在外部建立一個調用 db.transaction() 的函數。比如,我們可以讓錯誤函數是自包含的,并将其命名為“createToDoTable()”。
插入
為了讓代碼更加簡潔和安全,Web SQL Database API 允許你為 transaction.executeSql() 函數提供字元串資料,用以表示調用的 SQL 語句中的變量。我們使用以下的代碼進行示範:
db.transaction( function(tx) { tx.executeSql("INSERT INTO ToDo (label, timestamp) values(?, ?)", [label, new Date().getTime()], null, null); } );
在這個示例中,第一個參數中的兩個問号将被後面數組中對應的項替代。第一個是為該任務設定的标簽(也許是我們之前在代碼中定義的一個變量),以及調用函數生成的時間戳。
執行該查詢,其結果與下面語句類似:
INSERT INTO ToDo (label, timestamp) values ("Test", 1265925077487)
對結果進行處理
成功執行的函數對結果對象包含集合或行。每一清單示一個結果。該結果包含配置設定給它的一組值,表示該特定結果的資料庫中的每一列的值。通過調用 result.rows.item(i) 可以通路一個行,其中 i 是你想要查詢的行的指針。想要從一行中選擇一個值,你可以傳遞給該行一個數組格式的字元串指針,它表示你需要查詢的列。例如,如果想要标簽(label)列,我們可以調用 row['label']。
以下代碼使用結果對象來輸出一個查詢的結果:
db.transaction( function(tx) { tx.executeSql("SELECT * FROM ToDo", [], function(tx, result) { for(var i = 0; i < result.rows.length; i++) { document.write('' + result.rows.item(i)['label'] + '
'); } }, null); } );
結論
需要注意的是,如果不是絕對需要的情況,不要使用 Web SQL Database。這不是因為它們的技術高高在上,而是因為它們會讓你的代碼更加複雜。對于大多數情況,本地存儲或會話存儲就能夠完成相應的任務,尤其是你能夠保持對象狀态持久化的情況。
正如前面所說,通過這些HTML 5 Web SQL Database API 接口,你可以擷取許多功能。我相信,幾年以後會出現一些非常優秀的、建立在這些 API 之上的應用程式。