HTML5: API IndexedDB
Giới thiệu
Cơ sở dữ liệu là một tập hợp dữ liệu có tổ chức. Các cơ sở dữ liệu, như là cơ sở dữ liệu quan hệ lưu trữ dữ liệu dưới dạng bảng. Một bảng bao gồm các hàng và cột được sử dụng để lưu trữ dữ liệu. Việc trình bày dữ liệu từ bảng là ở dạng các bản ghi.
HTML5 đã giới thiệu một API Lưu trữ web mới có thể lưu trữ các cơ sở dữ liệu web cục bộ trong trình duyệt của người dùng. Tuy nhiên, các cơ sở dữ liệu web không giống như các cơ sở dữ liệu quan hệ về chức năng.
API Cơ sở dữ liệu có chỉ mục (Indexed DataBase API) là một đặc điểm kỹ thuật mới, còn được gọi với tên là API IndexedDB. Về cơ bản đó là kho đối tượng có thể được sử dụng để lưu trữ và thao tác dữ liệu ở phía máy khách. Lưu trữ đối tượng là cơ chế lưu trữ chính lưu trữ đối tượng trong cơ sở dữ liệu được quản lý cục bộ trong trình duyệt. Nó cho phép tạo ra kho đối tượng với từng loại cụ thể, trong đó các đối tượng vẫn có thể được tiếp tục sử dụng JavaScript. Như vậy, IndexedDB cho phép để tạo ra các ứng dụng web với khả năng truy vấn phong phú và có thể làm việc cả online và offline.
IndexedDB hỗ trợ hai loại bao gồm API đồng bộ (synchronous) và API không đồng bộ (asynchronous). API đồng bộ có thể được sử dụng với Web Worker, trong khi API không đồng bộ có thể được sử dụng cho các ứng dụng web. Hiện tại, chưa có trình duyệt chính nào hỗ trợ cho API đồng bộ.
API IndexedDB được thực hiện bằng cách sử dụng đối tượng window.indexedDB. Do các đặc điểm kỹ thuật hiện tại vẫn đang trong giai đoạn phát triển, nên các trình duyệt thực hiện đối tượng IndexedDB với các tiền tố của riêng mình. Ví dụ, trình duyệt Chrome sử dụng tiền tố -webkit, trong khi Mozilla hỗ trợ tiền tố -moz.
Bảng sau đây liệt kê các hỗ trợ trình duyệt đối với API IndexedDB.
IE (-ms) |
Firefox (-moz) |
Chrome (-webkit) |
Safari (-webkit) |
Opera (-o) |
iOS Safari |
---|---|---|---|---|---|
6.0 | 3.6 | - | - | - | 3.2 |
7.0 | 8.0moz | - | - | - | 4.0-4.1 |
8.0 | 9.0moz | 16.0webkit | 5.0 | - | 4.2-4.3 |
9.0 | 10.0moz | 17.0webkit | 5.1 | 11.6 | 5.0 |
10.0ms | 11.0moz | 18.0webkit | 6.0 | 12.0 | - |
- | 12.0moz | 19.0webkit | - | - | - |
Các cấu trúc cơ bản của API IndexedDB
API IndexedDB có một số cấu trúc cơ bản như sau:
- Cơ sở dữ liệu: Cơ sở dữ liệu IndexedDB gồm nhiều kho đối tượng. Mỗi cơ sở dữ liệu có chứa tên xác định nguồn gốc của cơ sở dữ liệu và một số phiên bản trong đó xác định thời gian tồn tại của cơ sở dữ liệu.
- Lưu trữ đối tượng: Lưu trữ đối tượng là cơ chế chính để lưu trữ dữ liệu trong một cơ sở dữ liệu. Chúng nắm giữ dữ liệu đã lưu trữ trong cơ sở dữ liệu ở dạng các bản ghi.
- Khóa: Mỗi bản ghi lưu lại trong cơ sở dữ liệu được xác định bằng khóa duy nhất.
- Giá trị: Các giá trị là những dữ liệu được lưu trữ trong các bản ghi.
- Đường dẫn khóa: Đường dẫn khóa là một chuỗi định nghĩa cách trình duyệt nên trích xuất khóa từ một giá trị. Khóa từ một giá trị có thể được trích xuất hoặc trong kho đối tượng hoặc chỉ số. Một chuỗi rỗng, một mã định danh JavaScript, ... là ví dụ về một số đường dẫn khóa hợp lệ.
- Chỉ số: Được sử dụng khi các dữ liệu từ kho đối tượng được lấy dựa trên một số giá trị khác ngoài khóa. Đó là một kho đối tượng chuyên dụng để tìm kiếm các bản ghi trong một kho đối tượng khác được gọi là kho đối tượng có tham chiếu.
- Giao tác: Bất kỳ việc thêm hoặc truy xuất dữ liệu nào trong một cơ sở dữ liệu đều được thực hiện bằng cách sử dụng giao tác. Nói cách khác, giao tác là một cơ chế được sử dụng để tương tác với cơ sở dữ liệu. Mỗi giao tác có một chế độ, phạm vi, và một danh sách yêu cầu. Chế độ này xác định loại giao tác có thể được thực hiện, phạm vi chỉ ra kho đối tượng mà giao tác sẽ được thực hiện trên đó, và cuối cùng là danh sách yêu cầu để xác định các yêu cầu đã được đưa ra.
- Yêu cầu: Các thao tác, chẳng hạn như đọc hoặc viết trên cơ sở dữ liệu được thực hiện bằng việc sử dụng một yêu cầu. Mỗi yêu cầu chứa các thuộc tính, chẳng hạn như cờ, đối tượng nguồn, kết quả, và lỗi.
- Con trỏ: Con trỏ là một cơ chế được sử dụng để lấy nhiều bản ghi từ một cơ sở dữ liệu.
- Dải khóa: Các bản ghi từ kho đối tượng và các chỉ số được lấy ra sử dụng các khóa hoặc dải khóa. Dải khóa đề cập đến việc truy xuất dữ liệu giữa các giới hạn đã chỉ định dựa trên các khóa.
Thực thi API IndexedDB
Các bước để thực hiện IndexedDB API trong ứng dụng web như sau:
- Mở một Cơ sở dữ liệu
- Cập nhật phiên bản của Cơ sở dữ liệu
- Tạo kho đối tượng
- Tạo một giao tác
- Thực hiện một số thao tác cơ sở dữ liệu, chẳng hạn như thêm và lấy thông qua việc mở một con trỏ
- Làm việc với các kết quả đã truy xuất
Mở một Cơ sở dữ liệu
Đoạn mã sau trình bày đoạn mã để mở cơ sở dữ liệu.
var request = indexedDB.open("CompanyDB", 1);
request.onsuccess = function (event) {
. . .
};
request.onerror = function (event) {
console.log("Lỗi IndexedDB: " + event.target.errorCode);
};
Ở đây, đoạn mã phát hiện sự hỗ trợ cho API IndexedDB trong các trình duyệt khác nhau và tạo ra đối tượng indexedDB. Đối tượng indexedDB có chứa một phương thức có tên là open() mở cơ sở dữ liệu CompanyDB.
Trong trường hợp, nếu cơ sở dữ liệu tồn tại, khi đó phương thức open() chỉ cần mở nó, nếu không tạo ra cơ sở dữ liệu đó.
Phương thức open() trả về đối tượng IDBRequest có tên là request. Đối tượng request cung cấp các bộ xử lý, chẳng hạn như thành success và error. Những bộ xử lý này được gọi tùy thuộc vào việc mở cơ sở dữ liệu thành công hay thất bại. Bộ xử lý onsuccess() có chứa một sự kiện thuộc loại success làm đối số của nó. Tương tự như vậy, bộ xử lý onerror() được gọi với sự kiện error làm đối số của nó.
Cập nhật phiên bản của Cơ sở dữ liệu
Sau khi cơ sở dữ liệu được mở ra, nó có thể được cấu trúc bằng cách cung cấp số phiên bản. Điều này giúp thiết lập cơ sở dữ liệu.
Số phiên bản sẽ được chỉ ra cho cơ sở dữ liệu trong hàm onsuccess().
Đoạn mã sau trình bày đoạn mã chỉ ra số phiên bản cho cơ sở dữ liệu CompanyDB.
setVrequest.onsuccess = function(event) {
. . .
}
Tạo kho đối tượng
Cấu trúc của cơ sở dữ liệu IndexedDB tạo điều kiện cho việc lưu trữ nhiều kho đối tượng. Nói cách khác, có thể có nhiều hơn một kho đối tượng trong cơ sở dữ liệu. Kho đối tượng được tạo ra sử dụng phương thức createObjectStore(). Các phương thức createObjectStore() nhận hai đối số cụ thể là: tên kho và đối tượng parameter. Đối tượng parameter được sử dụng để xác định thuộc tính tùy chọn mà quan trọng. Trong trường hợp này, đường dẫn khóa được định nghĩa được sử dụng để xác định các đối tượng duy nhất trong kho đối tượng. Ví dụ, kho nhân viên chứa thuộc tính id làm đường dẫn khóa, sẽ là duy nhất cho từng đối tượng và phải có mặt cho từng đối tượng.
Đoạn mã sau trình bày đoạn mã để tạo ra một kho đối tượng có tên là employee trong cơ sở dữ liệu CompanyDB.
var objectStore = db.createObjectStore("employee", {
keyPath: "id", autoIncrement: true });
for (i in employeeData) {
objectStore.put(employeeData[i]);
alert("Bản ghi đã thêm");
}
Đoạn mã tạo ra một mảng có tên là employeeData có chứa các giá trị tên và e-mail. Sau đó, phương thức createObjectStore() tạo ra kho nhân viên với đường dẫn khóa được đặt thành thuộc tính id. Đường dẫn khóa được sử dụng với tùy chọn autoIncrement tự động tạo ra id cho mỗi đối tượng. Tất cả các đối tượng riêng lẻ trong kho đối tượng được chỉ ra dựa trên id. Cuối cùng, vòng lặp for..in lưu trữ dữ liệu trong kho đối tượng employee.
Tạo một giao tác
Để thực hiện thao tác cơ sở dữ liệu, chẳng hạn như lấy dữ liệu từ kho đối tượng, IndexedDB cung cấp đối tượng IDBTransaction. Đối tượng này có thể được tạo ra trong ba chế độ cụ thể là: chỉ đọc, đọc ghi, và ảnh chụp. Chế độ đọc ghi được sử dụng để cập nhật đối tượng, trong khi đó chế độ chỉ đọc được sử dụng cho các thao tác khác.
Đoạn mã sau trình bày đoạn mã để lấy dữ liệu từ kho đối tượng employee sử dụng hàm get() của đối tượng transaction.
var request = trans.get(2);
request.onerror = function(event) {
//Xử lý lỗi!
};
request.onsuccess = function(event) {
//Làm gì đó với request.result!
alert("Tên: " + request.result.name);
};
Trong đoạn mã này, phương thức transaction() nhận hai tham số. Tham số thứ hai là tùy chọn. Tham số đầu tiên là danh sách các kho đối tượng được mở rộng bởi đối tượng transaction. Trong trường hợp này, có một kho đối tượng đơn lẻ được đặt tên là employee, được tạo ra trong cơ sở dữ liệu. Tham số thứ hai tùy chọn chỉ ra loại giao tác, có nghĩa là, chỉ đọc, đọc ghi, hoặc ảnh chụp. Ở đây, loại giao tác được định nghĩa là IDBTransaction.READ_WRITE. Loại này cho phép đọc cũng như viết trong cơ sở dữ liệu. Kho đối tượng employee được lấy từ đối tượng transaction trên đó các thao tác được thực hiện. Ở đây, phương thức get() được gọi trên đối tượng employee trả về giá trị so với đường dẫn khóa 2. Cuối cùng, kết quả của phương thức get() được lưu trữ trong đối tượng yêu cầu trên đó gọi lại các hàm, chẳng hạn như onsuccess và onerror được gọi.
Mở một con trỏ
Con trỏ được sử dụng để lấy nhiều bản ghi từ một kho đối tượng. Chúng có thể được sử dụng khi giá trị của đường dẫn khóa là không rõ. Chúng là một phần của một giao tác và được mở cho một kho đối tượng cụ thể.
Đoạn mã dưới đây trình bày đoạn mã để lấy nhiều bản ghi từ kho đối tượng employee.
store.openCursor().onsuccess = function(event) {
var cursor = event.target.result;
if (cursor) {
alert("Tên cho id " + cursor.key + " là " + cursor.value.name);
cursor.continue();
}
};
Ở đây, trong đoạn mã, đối tượng transaction được tạo ra cho kho đối tượng employee. Sau đó, hàm openCursor() được gọi trên kho đối tượng. Nếu con trỏ được mở ra thành công, một đối tượng con trỏ được trả về sẽ lấy dữ liệu từ kho đối tượng.
Đoạn mã sau đây trình bày đoạn mã đầy đủ để mở, tạo ra, và lấy dữ liệu từ kho đối tượng sử dụng API IndexedDB.
<html>
<head>
<meta charset="utf-8" />
<title>API IndexedDB</title>
<!--[if IE]>
<script src="http://html5shim.googlecode.com /svn/trunk/html5.js"></script>
<![endif]-->
<script>
//Phát hiện hỗ trợ cho IndexedDB API
var indexedDB = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB || window.msIndexedDB;
var IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction;
var db;
var transaction;
var store;
var objectStore;
var employeeData = [{ name: "John Smith", email: "john@company.com" }, { name: "Jill Patrick", email: "jill@company.com" }, { name: "Rock Ethan", email: "rock@company.com" }, { name: "Daniel Andrew", email: "daniel@company.com" }];
function initDb() {
var request = indexedDB.open("CompanyDB", 1);
request.onsuccess = function (evt) {
db = request.result;
var setVrequest = db.setVersion("1.99");
setVrequest.onsuccess = function(e) {
if(db.objectStoreNames.contains( "employee")) {
db.deleteObjectStore("employee");
alert ('Đối tượng employee đã bị xóa');
}
objectStore = db.createObjectStore("employee", {keyPath: "id", autoIncrement: true });
alert ('Đối tượng employee được tạo ra');
objectStore.createIndex("name", "name", { unique: false });
objectStore.createIndex("email", "email", { unique : true });
for (i in employeeData) {
objectStore.put(employeeData[i]);
alert('Đã thêm bản ghi');
}
};
};
request.onerror = function (evt) {
console.log("Lỗi IndexedDB: " + evt.target.errorCode);
};
}
function employee_details() {
store = db.transaction("employee"). objectStore("employee");
store.openCursor().onsuccess = function(event) {
var cursor = event.target.result;
if (cursor) {
alert("Tên cho id " + cursor.key + " là " + cursor.value.name);
cursor.continue();
}
};
}
function search_employee() {
trans = db.transaction(["employee"], IDBTransaction.READ_WRITE). objectStore("employee");
var request = trans.get(2);
request.onerror = function(event) { //Xử lý lỗi! };
request.onsuccess = function(event) {
//Làm việc với request.result
alert("Tên: " + request.result.name);
};
};
}
window.addEventListener( "DOMContentLoaded", initDb, false);
</script>
</head>
<body>
<input type="button" value="In chi tiết nhân viên" onclick="employee_details()"/> <br/>
<input type="button" value="Tìm kiếm nhân viên dựa trên khóa" onclick="search_employee()"/>
</body>
</html>
Việc thực hiện chương trình bắt đầu với việc tạo ra kho đối tượng employee, trong đó các bản ghi được thêm vào.
Cửa sổ thông báo hiển thị thông báo 'Đã thêm bản ghi' trong khi lưu trữ các bản ghi vào kho đối tượng. Sau đó, ứng dụng cho phép người dùng truy vấn cơ sở dữ liệu bằng cách bấm vào nút In chi tiết nhân viên và Tìm kiếm nhân viên dựa trên khóa.
Hình dưới đây trình bày kết quả cho kho đối tượng employee trong trình duyệt Chrome, khi người dùng bấm vào nút Tìm kiếm nhân viên dựa trên khóa.
Hạn chế của API IndexedDB
API IndexedDB API được sử dụng để lưu trữ dữ liệu phía máy khách, nhưng nó có một số hạn chế về thiết kế. Những hạn chế này như sau:
- Phân loại quốc tế hóa làm việc với phân loại dữ liệu chuỗi. Bởi cơ sở dữ liệu không tuân theo bất kỳ trật tự quốc tế nào để lưu trữ dữ liệu, nên phân loại quốc tế hóa không được API hỗ trợ.
- API IndexedDB không đồng bộ hóa cơ sở dữ liệu phía máy khách với các cơ sở dữ liệu phía máy chủ. Nếu cần thiết, khi đó đoạn mã cần phải được viết để thực hiện việc đồng bộ hóa phía máy chủ.
- API IndexedDB hỗ trợ truy vấn cơ sở dữ liệu phía máy khách, nhưng không hỗ trợ việc sử dụng các toán tử, chẳng hạn như toán tử LIKE được sử dụng trong Structured Query Language (SQL - Ngôn ngữ truy vấn có cấu trúc). SQL là ngôn ngữ truy vấn được sử dụng bởi các cơ sở dữ liệu phía máy chủ để thực hiện thao tác trong cơ sở dữ liệu đó.