indexedDB,非关系型数据库,W3C标准推荐
indexedDB是一种轻量级NOSQL数据库,是由浏览器自带。相比Web Sql更加高效,包括索引、事务处理和查询功能。
在HTML5本地存储中,IndexedDB存储的数据是最多的,不像webStorage的4M,IndexedDB存储空间是无上限且永久的。
indexedDB支持度情况:
http://caniuse.com/#search=indexdb
对于ios10以前的版本有下面这一条说明:
Partial support in Safari & iOS 8 & 9 refers to seriously buggy behavior as well as complete lack of support in WebViews.
部分支持Safari和iOS 8和9指的是严重的错误行为以及在WebViews完全缺乏支持。
Ok,如果,我们要用indexedDB,对于ios10之前的版本,那到底有多严重呢?
这个api里面有详细的介绍,其实基本的一些操作是可以用的。
https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API
在讨论过indexedDB是否可用后,我们来正式学习一下它:
indexedDB特点:
键值对存储:IndexedDB内部采用对象仓库(object store)存放数据。所有类型的数据都可以直接存入,包括JavaScript对象。在对象仓库中,数据以“键值对”的形式保存,每一个数据都有对应的键名,键名是独一无二的,不能有重复,否则会抛出一个错误。
异步:IndexedDB操作时不会锁死浏览器,用户依然可以进行其他操作,这与localStorage形成对比,后者的操作是同步的。异步设计是为了防止大量数据的读写,拖慢网页的表现。
支持事务:IndexedDB支持事务(transaction),这意味着一系列操作步骤之中,只要有一步失败,整个事务就都取消,数据库回到事务发生之前的状态,不存在只改写一部分数据的情况。
同域限制 :您也受到同域限制,每一个数据库对应创建该数据库的域名。来自不同域名的网页,只能访问自身域名下的数据库,而不能访问其他域名下的数据库。
储存空间大:IndexedDB的储存空间比localStorage大得多,一般来说不少于250MB。IE的储存上限是250MB,Chrome和Opera是剩余空间的某个百分比,Firefox则没有上限。
支持二进制储存: IndexedDB不仅可以储存字符串,还可以储存二进制数据。
indexedDB的主要方法:
1.创建数据库window.indexedDB.open()
我们可以这样创建或打开一个本地的数据库对象
var openRequest =window.indexedDB.open(name, version);
var db;
openRequest.onupgradeneeded = function(e) {
console.log("Upgrading...");}
openRequest.onsuccess = function(e) {
console.log("Success!");
db = e.target.result;
}
openRequest.onerror = function(e) {
console.log("Error");
console.dir(e);
}
(1)第一次打开数据库时,会先触发upgradeneeded事件,然后触发success事件。
(2)open方法返回的是一个对象(IDBOpenDBRequest),回调函数定义在这个对象上面。
(3)回调函数接受一个事件对象event作为参数,它的target.result属性就指向打开的IndexedDB数据库。
如图为chrom浏览器中的打开后的对象
2.indexedDB实例对象的方法—createObjectStore
createObjectStore方法用于创建存放数据的“对象仓库”(object store),类似于传统关系型数据库的表格。
db.createObjectStore("test", { keyPath: "email" });
db.createObjectStore("test2", { autoIncrement: true });
上面代码中的keyPath属性表示,所存入对象的email属性用作每条记录的键名(由于键名不能重复,所以存入之前必须保证数据的email属性值都是不一样的),默认值为null;autoIncrement属性表示,是否使用自动递增的整数作为键名(第一个数据为1,第二个数据为2,以此类推),默认为false。一般来说,keyPath和autoIncrement属性只要使用一个就够了,如果两个同时使用,表示键名为递增的整数,且对象不得缺少指定属性。
if(!db.objectStoreNames.contains("firstOS")) {
db.createObjectStore(“firstOS”);
}
上面代码先判断某个“对象仓库”是否存在,如果不存在就创建该对象仓库。
3.indexedDB实例对象的方法—transaction
transaction方法用于创建一个数据库事务。向数据库添加数据之前,必须先创建数据库务。
transaction方法返回一个事务对象,该对象的objectStore方法用于获取指定的对象仓库。
var transaction = db.transaction(["firstOS"],"readwrite");
var store = transaction.objectStore("firstOS");
transaction方法接受两个参数:
第一个参数是一个数组,里面是所涉及的对象仓库,通常是只有一个;
第二个参数是一个表示操作类型的字符串。readonly(只读)和readwrite(读写)。
transaction方法有三个事件,可以用来定义回调函数。
abort: 事务中断; complete: 事务完成; error: 事务出错。
transaction.oncomplete = function(event) {
// some code
};
4.transaction对象—添加数据: add方法
获取对象仓库以后,就可以用add方法往里面添加数据了。
var transaction = db.transaction(["firstOS"],"readwrite");
var store = transaction.objectStore(“firstOS”);
var o = {p: 123};
var request = store.add(o,1);
request.onerror = function(e) {
console.log("Error",[e.target.error.name](http://e.target.error.name));
}
request.onsuccess = function(e) {
console.log("数据添加成功!");
}
add方法的第一个参数是所要添加的数据,第二个参数是这条数据对应的键名(key),上面代码将对象o的键名设为1。如果在创建数据仓库时,对键名做了设置,这里也可以不指定键名。上述操作也可以写成链式的
下图为一个学生对象添加到数据库中在chrom浏览器中的存储情况:
5.transaction对象—其他方法
(1)更新记录:put方法,更新数据仓库中的对象
var o = { p:456 };
var request = store.put(o);
(2)读取数据:get方法,通过键值key获取数据仓库中的对象
var request = store.get(key);
(3)删除记录:delete方法,通过键值key删除数据仓库中的对象
var request = store.delete(key);
(4)清空数据仓库:clear方法,删除整个object store中的数据
var request = store.clear();
(5)遍历数据:openCursor方法,利用游标遍历数据,下面详细讲一下这个方法
var request = store.openCursor();
6.transaction对象—遍历数据:openCursor方法
(1)利用游标遍历数据
var request = store.openCursor();
request.onsuccess = function(e) {
var cursor = e.target.result;
if(cursor && cursor !== null) {
console.log("Key", cursor.key);
console.dir("Data", cursor.value);
cursor.continue();
}else{
console.log(“遍历结束");
}
request.onerror = function(e) {
console.log("Error",[e.target.error.name](http://e.target.error.name));
}
(2)利用游标 返回 或 删除 指定的数据
var request = store.openCursor();
var data = [];
request.onsuccess = function(e) {
var cursor = e.target.result;
if(cursor && cursor !== null) {
if(cursor.[value.name](http://value.name) === ‘jack’){
data.push(cursor.value);
// 或者在此删除数据
// cursor.delete();
}
cursor.continue();
}else{
console.log(“遍历结束”);
// 在此resolve(data);
}
request.onerror = function(e) {
console.log("Error",[e.target.error.name](http://e.target.error.name));
}
7.indexedDB实例对象的方法—索引createIndex与index
如图一个学生对象,创建索引:
(1)createIndex方法用于创建索引。
var store = db.createObjectStore('student',{keyPath: “id”});
store.createIndex(‘emailIndex’,'email',{unique:true}); //邮箱
store.createIndex(‘classIndex’,'class',{unique:false}); //班级
store.createIndex(‘sexIndex','sex',{unique:false}); //性别
store.createIndex(‘classSexIndex’,['class','sex'],{unique:false}); //班级+性别
(2)createIndex方法接受三个参数,第一个是索引名称,第二个是建立索引的属性名,第三个是参数对象,用来设置索引特性,unique表示索引所在的属性是否有唯一值。
var index = store.index(“emailIndex");
index.get('jack').onsuccess=function(e){
var student=e.target.result;
console.log(student.id);
}
Index.get用来获取唯一索引,如果不是唯一的,就要用到游标
8.IDBKeyRange对象—指定游标范围
索引的有用之处,还在于可以指定读取数据的范围。这需要用到浏览器原生的IDBKeyRange对象。
IDBKeyRange对象的作用是生成一个表示范围的Range对象。生成方法有四种:
lowerBound方法:指定范围的下限。
upperBound方法:指定范围的上限。
bound方法:指定范围的上下限。
only方法:指定范围中只有一个值。
var r1 = IDBKeyRange.upperBound(x); All keys ≤ x
var r2 = IDBKeyRange.upperBound(x, true); All keys < x
var r3 = IDBKeyRange.lowerBound(y); All keys ≥ y
var r4 = IDBKeyRange.lowerBound(y, true); All keys > y
var r5 = IDBKeyRange.bound(x, y); All keys ≥ x && ≤ y
var r6 = IDBKeyRange.bound(x, y, true, true); All keys > x &&< y
var r7 = IDBKeyRange.bound(x, y, true, false); All keys > x && ≤ y
var r8 = IDBKeyRange.bound(x, y, false, true); All keys ≥ x &&< y
var r9 = IDBKeyRange.only(z); // The key = z
如下图所示的方法,便可
利用索引查询与特定条件匹配的所有记录
利用sexIndex查找所有男同学的信息
getIndex(‘student’,’sexIndex’,’male’);
利用classSexIndex查找所有二年六班的女同学的信息
getIndex(‘student’,’classSexIndex’,[‘二年六班’,’female’]);
由此可见,在项目中使用,对于数据库的封装饰很有必要的,
下面我在项目中封装的indexedDB的一些部分,仅供参考:
dbModule.factory('iDbService',["$http", "$q", function ($http,$q) {
var myDB = {
name : 'localIndexDB',
version : 1,
db : null
};
var openDB = function (name, version, stores) {
console.log('openDB');
var d = $q.defer();
var _name = [myDB.name](http://myDB.name) || name;
var _version = myDB.version || version;
//打开数据库
var result = window.indexedDB.open(_name,_version);
//错误
result.onerror = function(e){
console.log("Open DB Error!");
d.reject("error");
};
//正确打开
result.onsuccess = function(e){
myDB.db = e.target.result ;
console.log('success');
d.resolve("success");
};
//数据库版本变更
result.onupgradeneeded = function(e){
myDB.db = e.target.result ;
if(!myDB.db.objectStoreNames.contains('users')){
myDB.db.createObjectStore('users',{keyPath: "id"});
}
if(!myDB.db.objectStoreNames.contains('infos')){
myDB.db.createObjectStore('infos',{autoIncrement: true});
}
if(!myDB.db.objectStoreNames.contains('problem')){
var store = myDB.db.createObjectStore('problem',{keyPath: "fqid"}); // 建数据仓库object store
store.createIndex('minTaskIndex',['fmid','fstate'],{unique:false}); // 创建索引
}
console.log('upgradeneeded');
d.resolve("upgradeneeded");
};
return d.promise;
};
openDB();
// 通过key查询数据
var get = function (storeName,key) {
var d = $q.defer();//promise
var _db = myDB.db;
var transaction = _db.transaction(storeName,'readonly');
var store = transaction.objectStore(storeName);
var result = store.get(key);
result.onsuccess = function (e) {
var data = e.target.result;
console.log('indexDb 一条数据查询结果为:');
console.log(data);
d.resolve(data);
};
result.onerror = function (e) {
d.reject();
};
return d.promise;
};
// 查询一个object store的所有数据
var getAll = function(storeName){
var d = $q.defer();//promise
var _db = myDB.db;
var transaction = _db.transaction(storeName,'readonly');
var store = transaction.objectStore(storeName);
var result = store.openCursor(); // 打开一个游标
var data = [];
result.onsuccess = function (e) {
var cursor = e.target.result;
if (cursor && cursor !== null) {
var problem = cursor.value;
var jsonStr = JSON.stringify(problem);
data.push(problem);
cursor.continue();
}else {
console.log('indexDb 一张表数据查询结果为:');
// console.log(data);
d.resolve(data);
}
};
result.onerror = function (e) {
d.reject();
};
return d.promise;
};
// 根据索引,查询与特定条件匹配的所有记录
var getIndex = function(storeName,indexName,params){
var d = $q.defer();//promise
console.log('索引查询的params');
console.log(params);
var _db = myDB.db;
var transaction = _db.transaction(storeName,'readonly');
var store = transaction.objectStore(storeName);
var index = store.index(indexName);
var range = IDBKeyRange.only(params);
var result = index.openCursor(range); // 打开一个游标
var data = [];
result.onsuccess = function (e) {
var cursor = e.target.result;
if (cursor && cursor !== null) {
var problem = cursor.value;
var jsonStr = JSON.stringify(problem);
data.push(problem);
cursor.continue();
}else {
console.log('indexDb' + storeName + '表中' + indexName + '索引,数据查询结果为:');
// console.log(data);
d.resolve(data);
};
result.onerror = function (e) {
d.reject();
};
return d.promise;
};
// 新增数据
var add = function (storeName,value) {
var d = $q.defer();
var _db = myDB.db;
var transaction = _db.transaction(storeName,'readwrite');
var store = transaction.objectStore(storeName);
var result = store.add(value);
result.onsuccess = function(e) {
console.log("create note success!");
console.log(myDB);
d.resolve();
};
result.onerror = function(e) {
console.log("can't create database,error:" + result.error);
d.reject();
};
return d.promise;
};
// 插入更新数据
var put = function (storeName,value) {
var d = $q.defer();
var _db = myDB.db;
var transaction = _db.transaction(storeName,'readwrite');
var store = transaction.objectStore(storeName);
var result = store.put(value);
result.onsuccess = function(e) {
console.log("create note success!");
d.resolve();
};
result.onerror = function(e) {
console.log("can't create database,error:" + result.error);
d.reject();
};
return d.promise;
};
// 通过key删除对象
var remove = function (storeName,key) {
var _db = myDB.db;
var transaction = _db.transaction(storeName,'readwrite');
var store = transaction.objectStore(storeName);
var result = store.delete(key);
result.onsuccess = function (e) {
console.log(e);
};
result.onerror = function (e) {
console.log(e);
};
};
// clear方法可以清空object store
var clear = function (storeName) {
var _db = myDB.db;
var transaction = _db.transaction(storeName,'readwrite');
var store = transaction.objectStore(storeName);
var result = store.clear();
result.onsuccess = function (e) {
console.log(e);
};
result.onerror = function (e) {
console.log(e);
};
};
// 删除与特定条件匹配的所有记录
var removeData = function(storeName,arg,value){
var d = $q.defer();//promise
console.log('删除匹配值的arg+value');
console.log(arg);
console.log(value);
var _db = myDB.db;
var transaction = _db.transaction(storeName,'readwrite');
var store = transaction.objectStore(storeName);
var result = store.openCursor(); // 打开一个游标
result.onsuccess = function (e) {
var cursor = e.target.result;
if (cursor && cursor !== null) {
if(cursor.value[arg[0]] === value[0] && cursor.value[arg[1]] === value[1]) {
var request = cursor.delete();
request.onsuccess = function() {
console.log('Deleted this cursor data.');
};
} else {
console.log('131231312');
}
cursor.continue();
}else {
console.log('Entries displayed.');
d.resolve();
}
};
result.onerror = function (e) {
d.reject();
};
return d.promise;
};
return {
openDB: openDB,
get: get,
getAll: getAll,
getIndex: getIndex,
add: add,
put: put,
remove: remove,
clear: clear,
removeData: removeData
};
}]);