浏览器数据库 indexedDB

更新日期: 2019-11-17阅读: 1.7k标签: 数据库

1.关于indexedDB

indexedDB用于在浏览器端存储大量的结构化数据。对比于其他的浏览器存储技术(cookie,localStorage),indexedDB具有以下优点:

1.存储空间特别大,远超cookie与localStorage;
2.可以通过索引实现高性能搜索;
3.所有操作完全异步执行;

另外,indexedDB还具备以下特点:

1.非关系型数据库,不能使用结构化查询语言(SQL);
2.数据以健值对的方式存储;
3.事务模式数据库,任何操作都发生在事务中;
4.遵循同源策略;

基于以上的特点,indexedDB非常适合于需要在客户端存储大量数据的网站,更是和基于PWA的WebAPP相契合。
关于indexedDB的浏览器兼容性,可以参照:https://www.caniuse.com/#search=indexeddb


2.基本使用

1.创建/打开数据库

var request = window.indexedDB.open(DBName, version);
函数包含两个参数,第一个为数据库名称,第二个为数据库版本号(整数)。
使用open函数创建/打开数据库时,根据参数不同,可能存在以下几种情况:

1.指定名称的数据库不存在,则新建数据库,默认版本号为1;
2.指定名称的数据库已存在,省略版本号,则打开当前版本号的数据库;
3.指定名称的数据库已存在,版本号大于数据库实际版本号,则进行数据库升级;

open函数返回一个IDBOpenDBRequest对象,该对象通过以下三种事件进行数据库打开操作的后续处理:

    var db;
    //打开数据库成功事件,返回数据库对象实例(IDBDatabase对象的实例)
    request.onsuccess = function(event){
        db = request.result;
    } 
    //打开数据库失败事件
    request.onerror = function(event){
        //打印报错信息
    }
    //数据库升级事件,仅在创建数据库/数据库升级时触发
    request.onupgradeneeded = function(event){
        //缓存IDBDatabase接口,用于后续创建/删除对象存储空间(对象仓库,类比于关系型数据库的表)
        var _db = event.target.result;
        //创建/删除对象存储空间的操作
        ...
    }

无论是打开数据库,或者是更新数据库,所有后续的查询、插入、删除等操作,都要通过onsuccess函数返回的IDBDatabase实例去调用。
所有对数据库schema的更新只能在onupgradeneeded事件中进行。


2.创建对象仓库及索引

indexedDB中,使用对象仓库来存储具体的数据,其功能类似与关系型数据库的表。
不同于关系型数据库的表结构,对象仓库中数据的存储结构如下图所示:
1576657854506.jpg
Key代表对象仓库主键,而具体的数据,采用对象的方式存储在Value中。

在数据库新建操作完成后,接下来就是新建对象仓库,并创建索引了。
对象仓库的创建 与 该对象仓库中索引的创建方法为:

    request.onupgradeneeded = function(event){
        var _db = event.target.result;
        var objectStore;
        //首先,可以先判断是否存在重名的对象仓库
        if(!_db.objectStoreNames.contains('person')){
            //创建对象仓库,第一个参数为仓库名称,第二个参数可选
            objectStore = _db.createObjectStore('person', {
                //主键
                keyPath: 'id',
                //可选,主键自增
                autoIncrement: true
            });
            //创建索引,第一个参数为索引名称,第二个参数为索引所在的属性,可以使用数组将多个属性设置为一个索引
            //第三个参数为可选参数,指定该属性的值是否要求唯一
            objectStore.createIndex('phone_index', 'phone', {unique: true});
        }
    }

关于为什么要创建索引:对数据库不是很熟悉,或者对关系型数据库有所了解,而对非关系型数据库不太了解的同学,可能会对索引有所疑惑。索引的意义,在于快速查询数据,就如同书的目录一样,通过目录可以快速找到我们想看内容的页数。在关系型数据库中,查询数据,可以使用SQL语句:select * from person wherephone="13910002000",而在非关系型数据库中,没有SQL,如果没有索引,我们只能根据主键去查询数据。只有建立索引之后,才能将索引对应的属性作为查询条件进行数据查询。  

进行数据库版本升级的情况下,之前版本的对象仓库是依然存在的,无需重复创建。如果需要更新对象仓库属性,需要先删除原有对象仓库,再重新创建。


3 创建事务与获取IDBObjectStore对象

indexedDB是事务模式数据库,所有的数据操作,都要在事务中进行。因此在学习数据的增删改查操作之前,我们先看如何新建事务,并获取IDBObjectStore对象。

1.创建事务

var transaction = db.transaction(storeNames, mode);
使用IDBDatabase对象实例来调用事务创建函数,第一个参数,可以是一个对象仓库名称的字符串(仅使用到一个对象仓库),或者是一个对象仓库名称的数组(用到一个或多个对象仓库)。第二个参数,指定事务中可以进行的操作类型,包括readonly(只读)和readwrite(可读写)。

2.获取IDBObjectStore对象

var objectStore = transaction.objectStore(storeName);
事务创建结束后,获取需要进行操作的对象仓库——IDBObjectStore对象,在这个对象下,进行具体的增删改查操作。
对于多事务处理的情况,事务在被创建的时候就已经开始了,因此对对象仓库的操作顺序,由事务创建的顺序决定。样例参见https://developer.mozilla.org/zh-CN/docs/Web/API/IDBTransaction


4 向对象仓库中新增数据

    function add(){
        //创建事务
        var transaction = db.transcation('person', 'readwrite');
        //获取IDBObjectStore对象
        var objectStore = transaction.objectStore('person');
        //执行新增操作并返回IDBRequest对象,如果主键设置为自增,参数可不包含主键
        var request = objectStore.add({'name': '张三', 'phone': '13910002000'});
        
        request.onsuccess = function(event){
            //插入成功
            //返回新增数据的主键
            console.log(request.result);
        }
        
        request.onerror = function(event){
            //插入失败,失败的原因可以在event中查看
        }
    }
    add();


5 删除对象仓库中的一条数据

    function remove(){
        //创建事务
        var transaction = db.transcation('person', 'readwrite');
        //获取IDBObjectStore对象
        var objectStore = transaction.objectStore('person');
        //执行删除操作并返回IDBRequest对象,函数的参数为要删除数据的主键,数据类型为Number,或为IDBKeyRange
        var request = objectStore.delete(1);
        
        request.onsuccess = function(event){
            //删除成功
        }
        
        request.onerror = function(event){
            //删除失败
        }
    }
    remove();


6 查询对象仓库中的一条数据

查询可以依据主键值进行,或者通过创建的索引进行。

1.通过主键进行查询
    function get(){
        //创建事务
        var transaction = db.transcation('person', 'readonly');
        //获取IDBObjectStore对象
        var objectStore = transaction.objectStore('person');
        //执行查询操作并返回IDBRequest对象,函数的参数为要查询数据的主键,数据类型为Number,或为IDBKeyRange
        var request = objectStore.get(1);
        
        request.onsuccess = function(event){
            //查询成功
            //返回查询结果,满足查询条件的数据对象
            console.log(request.result);
        }
        
        request.onerror = function(event){
            //查询失败
        }
    }
    get();
2.通过索引进行查询

通过创建的索引进行查询时,需要首先通过IDBObjectStore对象打开某个名称的索引,获取IDBIndex对象,再通过IDBIndex对象进行查询操作。

    function get(){
        //创建事务
        var transaction = db.transcation('person', 'readonly');
        //获取IDBObjectStore对象
        var objectStore = transaction.objectStore('person');
        //获取IDBIndex对象,参数为索引的名称
        var index = objectStore.index('phone_index');
        //执行查询操作并返回IDBRequest对象,函数的参数为要查询数据,或为IDBKeyRange
        var request = index.get('13910002000');
        
        request.onsuccess = function(event){
            //查询成功
            //返回查询结果,满足查询条件的数据对象
            console.log(request.result);
        }
        
        request.onerror = function(event){
            //查询失败
        }
    }
    get();


7 查询对象仓库中的多条数据

查询多条数据时,有两种方式,第一种是通过IDBObjectStore对象或者IDBIndex对象的getAll方法(indexedDB 2.0),第二种是通过游标进行数据遍历。

1.使用getAll方法
    function getAll(){
        //创建事务
        var transaction = db.transcation('person', 'readonly');
        //获取IDBObjectStore对象
        var objectStore = transaction.objectStore('person');
        //获取IDBIndex对象,参数为索引的名称
        var index = objectStore.index('age_index');
        //执行查询操作并返回IDBRequest对象
        //函数的第一个参数为要查询数据,或为IDBKeyRange,可选,如果不传则返回所有数据
        //函数的第二个参数为要返回的数据数量,可选,如果不加则返回所有满足条件的数据,数据类型为Number,整数
        var request = index.getAll('20', 10);
        
        request.onsuccess = function(event){
            //查询成功
            //返回查询结果,满足查询条件的数据对象数组
            console.log(request.result);
        }
        
        request.onerror = function(event){
            //查询失败
        }
    }
    getAll();
2.使用游标遍历数据
    function readAll(){
        //创建事务
        var transaction = db.transcation('person', 'readonly');
        //获取IDBObjectStore对象
        var objectStore = transaction.objectStore('person');
        //openCursor函数拥有两个可选参数,第一个参数为查询条件
        //第二个参数为查询方向,默认为next,还可以设置为prev
        //项目基本没有使用这种方式遍历数据,所以具体参数设置/是否可以使用索引等尚未验证,等以后补充
        objectStore.openCursor().onsuccess = function(event){
            var cursor = event.target.result;
            if (cursor) {
                console.log('Id: ' + cursor.key);
                console.log('Name: ' + cursor.value.name);
                console.log('Age: ' + cursor.value.phone);
                cursor.continue();
            } else {
                console.log('没有更多数据了!');
            }
        }
    }
    readAll();


8 更新对象仓库中的一条数据

    function update(){
        //创建事务
        var transaction = db.transcation('person', 'readwrite');
        //获取IDBObjectStore对象
        var objectStore = transaction.objectStore('person');
        //执行更新操作并返回IDBRequest对象,函数的参数要更新的数据,包含主键
        var request = objectStore.put({'id': 1, 'name': '张三', 'phone': '13910002000'});
        
        request.onsuccess = function(event){
            //更新成功
        }
        
        request.onerror = function(event){
            //更新失败
        }
    }
    update();


3.复杂逻辑或问题的解决

通过以上部分的介绍,我们可以掌握indexedDB的基本操作,完成一些简单的、基本的业务需求。然而,我们的业务需求往往不会这么简单,这个部分,根据我在项目中遇到的一些问题,总结一下较为复杂的业务逻辑处理方式。如果各位大拿有更好的处理方式,也希望不吝赐教。

1 关于函数的封装

在上边写的例子中,为了简单的介绍indexedDB操作的各个方法,并没有对其进行严谨的封装。在实际生产应用中,增删改查的操作都应该做好封装,方便复用。简单一点的封装可以把数据库对象、要查询条件对象仓库名称、查询条件、查询成功回调和查询失败回调等设置为函数的参数。好一点的方法,可以考虑使用Promise进行相关操作的封装。

2 关于打开数据库

所有的数据库操作,都需要等待数据库打开成功的回调,拿到IDBDatabase对象的实例之后,才能够进行操作。对于需要用户去操作的情况,用户需要填写数据,或者填写查询数据,等这些耗时的操作完成时,打开数据库操作一般已经完成了,因此不用做什么特别处理也不会出现问题。
但是在页面初始化就需要拿数据的情况,由于打开数据库的操作是异步的,很有可能出现数据库打开还未成功,请求数据库数据的函数就已经开始执行了,导致数据获取失败。因此,比较稳妥的方式,应该是当打开数据库这个异步操作成功执行成功回调时,使用EventEmitter等库进行一个事件触发,让监听这个事件的函数知晓indexedDB已经准备完毕,可以进行相关操作。

3 关于连表查询的问题

在关系型数据库中,可以通过SQL语句完成连表查询,而在indexedDB中,由于没有SQL,无法进行连表查询的操作。如果想要查询到更多的数据,目前我只能通过数据冗余去完成。这就要求在设计数据库的时候,提前把相关的功能逻辑考虑到。

4 关于IDBKeyRange

在基础操作介绍中,对于对象仓库的增删改查中,尤其是在查询操作,函数的参数出了可以为主键值、具体的查询值等,还可以为一个IDBKeyRange对象实例。通过使用IDBKeyRange,我们可以把查询条件约束到一个范围中。IDBKeyRange提供的方法和对应的查询值约束范围包括:

IDBKeyRange.upperBound(x)              keys <= x
IDBKeyRange.upperBound(x, ture)        keys < x
IDBKeyRange.lowerBound(y)              keys >= x
IDBKeyRange.lowerBound(y, ture)        keys > x
IDBKeyRange.bound(x, y)                keys >= x && keys <= y
IDBKeyRange.bound(x, y, true, ture)    keys > x && keys < y
IDBKeyRange.bound(x, y, true, false)   keys > x && keys <= y
IDBKeyRange.bound(x, y, false, true)   keys >= x && keys < y
IDBKeyRange.only(z)                    keys = z

使用方法为:

    function getAll(){
        //创建事务
        var transaction = db.transcation('person', 'readonly');
        //获取IDBObjectStore对象
        var objectStore = transaction.objectStore('person');
        //获取IDBIndex对象,参数为索引的名称
        var index = objectStore.index('age_index');
        //获取年龄大于等于10岁小于等于20岁的数据
        var request = index.getAll(IDBKeyRange.bound(10, 20));
        
        request.onsuccess = function(event){
            //查询成功
            //返回查询结果,满足查询条件的数据对象数组
            console.log(request.result);
        }
        
        request.onerror = function(event){
            //查询失败
        }
    }
    getAll();
5 关于多条件查询

在关系型数据库中,可以通过以下SQL语句完成多条件查询:
select * from person where age = 20 AND name = '张三'
在indexedDB中,没有提供多条件查询的直接方法,想要实现多条件查询,目前一个可行的方法为,创建多属性索引,并借助IDBKeyRange进行查询。

    //创建数据仓库时
    request.onupgradeneeded = function(event){
        var _db = event.target.result;
        var objectStore;
        if(!_db.objectStoreNames.contains('person')){
            objectStore = _db.createObjectStore('person', {
                keyPath: 'id',
                autoIncrement: true
            });
            //创建一个多属性索引
            objectStore.createIndex('multi_index', ['name', 'age'], {unique: false});
        }
    }
    
    //进行查询时
    function getAll(){
        var transaction = db.transcation('person', 'readonly');
        var objectStore = transaction.objectStore('person');
        var index = objectStore.index('multi_index');
        var request = index.getAll(IDBKeyRange.only(['张三', 20]));
        
        request.onsuccess = function(event){
            console.log(request.result);
        }
        
        request.onerror = function(event){
        }
    }
    getAll();
6 关于数据库清除

一旦清楚了浏览器缓存,一切数据都将会被清除。所以,要么核心业务不要过度依赖于indexedDB,要么自己整一套方法机制进行数据的定期向服务器同步,要么。。。你跟用户说一下,不要让他没事清缓存。。。T-T
另外,在隐私模式下,推出浏览器之后,数据也会被清理(没有验证过)。


4. 结语

作为一个以关系型数据库开始学习的人,刚上手去做indexedDB的时候,由于是第一次接触NOSQL,不怎么适应,很多逻辑都已经被关系型数据库和SQL语句给固化了,结果在数据库设计和使用上走了很多弯路,特别蓝瘦。好在我遇到的坑,都已经跳出去了。。。

原文:https://segmentfault.com/a/1190000021329381

链接: https://www.fly63.com/article/detial/6909

数据库的选择:数据库种类那么多,该如何选择?

技术真的是日新月异,Web 网站已经脱离之前的静态网站的体系,转而使用动态语言搭建的动态网站。这也衍生出一个问题:该如何存储数据了?数据库就应运而生,它的作用是提供存储数据的容器。方便 web 网站进行存储、查询、更新等。

数据库的常用sql操作

数据库操作,不管是服务端、前端、移动端,都或多或少的会涉及到数据的存储、查询、修改。所以作为一名开发者,数据库操作也是开发必备的一项技能。本文是对数据库中经常用到的一些写法与及函数的归纳总结

B树和哈希索引的比较

了解B树和哈希数据结构有助于预测查询在这些使用不同索引数据结构的存储引擎上的执行情况,特别是对于MEMORY存储引擎,它是允许您选择B树或哈希作为索引的存储引擎。

使用 TypeScript 访问 MySQL 数据库

TypeScript 已经成为一个强大的 Web 应用程序开发环境,在与标准 JavaScript 保持一致的同时,提供了显著的改进。在本文中,我们将深入探讨使用 TypeScript 相关的细节

MyISAM和InnoDB的比较

MyISAM:不支持事务,而且也不支持外键,但是每次查询都是原子的,支持表级锁,即每次操作是对整个表加锁;InnoDb:支持ACID的事务,支持事务的四种隔离级别;

MongoDB的特性、使用原理

MongoDB是一个基于分布式文件存储的数据库。由C++语言编写。旨在为WEB应用提供可扩展的高性能数据存储解决方案。 具有高性能、易部署、易使用,存储数据非常方便等特点

传统数据库不适合现代企业架构了?

它还是我们如今看到的更广泛的技术趋势背后的统一思想,这些技术包括机器学习、物联网、无处不在的 SaaS 以及云计算。这些趋势都在用不同的方法使软件变得更丰富和功能强大,并在企业间扩大其影响范围

IndexedDB:浏览器里的本地数据库

在现代浏览器的本地存储方案中,indexedDB 是一项重要的能力组成, 它是可以在浏览器端使用的本地数据库,可以存储大量数据,提供接口来查询,还可以建立索引,这些都是其他存储方案 Cookie 或者 LocalStorage 无法提供的能力

数据库自增ID用完了会怎样?

对DBA来说这应该是送分题吧。而我是突如其来的想法想测试下的。正常来说程序员是不会关心自增ID用完的情况的。以 Mysql 为例,它支持的最大的整型是unsigned bigint

node如何连接数据库?

node连接数据库的方法:使用命令npm install mysql --save安装mysql的软件包,在项目文档中使用client.connect()即可连接数据库。下载MySQL :MySQL Downloads,并进行安装。安装完,会引导你对数据库进行配置,设置root密码以及创建普通用户以及密码

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!