indexDB讲解与封装

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特点:

  1. 键值对存储:IndexedDB内部采用对象仓库(object store)存放数据。所有类型的数据都可以直接存入,包括JavaScript对象。在对象仓库中,数据以“键值对”的形式保存,每一个数据都有对应的键名,键名是独一无二的,不能有重复,否则会抛出一个错误。

  2. 异步:IndexedDB操作时不会锁死浏览器,用户依然可以进行其他操作,这与localStorage形成对比,后者的操作是同步的。异步设计是为了防止大量数据的读写,拖慢网页的表现。

  3. 支持事务:IndexedDB支持事务(transaction),这意味着一系列操作步骤之中,只要有一步失败,整个事务就都取消,数据库回到事务发生之前的状态,不存在只改写一部分数据的情况。

  4. 同域限制 :您也受到同域限制,每一个数据库对应创建该数据库的域名。来自不同域名的网页,只能访问自身域名下的数据库,而不能访问其他域名下的数据库。

  5. 储存空间大:IndexedDB的储存空间比localStorage大得多,一般来说不少于250MB。IE的储存上限是250MB,Chrome和Opera是剩余空间的某个百分比,Firefox则没有上限。

  6. 支持二进制储存: 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浏览器中的打开后的对象

indexDB讲解与封装_第1张图片
image.jpeg

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浏览器中的存储情况:

indexDB讲解与封装_第2张图片
image.jpeg
indexDB讲解与封装_第3张图片
image.jpeg

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

如图一个学生对象,创建索引:

indexDB讲解与封装_第4张图片
image.jpeg

(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’]);

由此可见,在项目中使用,对于数据库的封装饰很有必要的,

indexDB讲解与封装_第5张图片
image.jpeg

下面我在项目中封装的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
  };
}]);

你可能感兴趣的:(indexDB讲解与封装)