# 基本概念
# 用 Excel 类比数据库
在使用 IndexedDB 前,你首先需要懂基本的数据库概念
这里用 Excel 类比,简单介绍数据库的基本概念,不做太深入的讨论
需要了解四个基本概念,以关系型数据库为例
- 数据库 Database
- 数据表 Table(IndexedDB 中叫 ObjectStore)
- 字段 Field
- 事务 Transaction
(虽然 IndexedDB 算不上关系型数据库,但概念都是相通的)
假设清华和北大各自需要建一个数据库,用来存各自学生与教工的信息,假设命名为
- 清华:
thu
- 北大:
pku
这样,清北之间的数据就可以相互独立
然后,我们再到数据库里建表
student
表,储存学生信息stuff
表,储存教工信息
数据表(Table)是什么?说白了,就是一个类似于 Excel 表一样的东西
比如 student
表,可以长这样:
上面的 学号、姓名、年龄、专业 就是数据表的字段
当我们想往 student
表添加数据时,就需要按照规定的格式,往表里加数据(关系型数据库的特点,而 IndexedDB 允许不遵守格式)
数据库也给我们提供了方法,当我们知道一个学生的学号(id),就可以在非常短的时间内,在表里成千上万个学生中,快速找到这个学生,并返回他的完整信息
也可以根据 id 定位,对该学生的数据进行修改,或者删除
id 这种每条数据唯一的值,就可以被用来做主键(primary key),主键在表内独一无二,无法添加相同主键的数据
增、删、改、查这些操作,都需要通过事务 Transaction 来完成
- 如果事务中任何一个操作没有成功,整个事务都会回滚
- 在事务完成之前,操作不会影响数据库
- 不同事务之间不能互相影响
举个例子,当你发起一个事务,想利用这个事务添加两个学生,如果第一个学生添加成功,但是第二个学生添加失败,事务就会回滚,第一个学生将根本不会在数据库中出现过
事务在银行转账这种场景非常有用:如果转账中任何一步失败了,整个转账操作就和没发生过一样,不会造成任何影响
在同一个 Excel 文件(数据库)中,我们除了 student
表,还可以有 stuff
表(同一个数据库中有了两个不同的数据表):
然后,清华和北大各自分一个 Excel 文件,就相当于分了两个数据库
总而言之,不扯数据库各种难理解的概念,我们其实完全可以用 Excel 来类比数据库
- 一个 Excel 文件就是一个 Database
- 一个 Excel(Database)里可以有很多不同表格(数据表 Table)
- 表格的列的名称其实就是字段
如果你是新手,用 Excel 类比理解数据库完全没问题,足以使用 IndexedDB 了
虽然说 IndexedDB 使用 key-value 的模式储存数据,但你也完全可以用数据表 Table 的模式来看待它
# 原生 IndexedDB 的使用
使用 IndexedDB 的第一步是打开数据库:
const request = window.indexedDB.open('pku');
上面这个操作打开了名为 pku
的数据库,如果不存在,浏览器会自动创建
然后 request
上有三个事件:
var db; // 全局 IndexedDB 数据库实例
request.onupgradeneeded = function (event) {
db = event.target.result;
console.log('version change');
};
request.onsuccess = function (event) {
db = request.result;
console.log('db connected')
};
request.onblocked = function (event) {
console.log('db request blocked!')
}
request.onerror = function (event) {
console.log('error!');
};
IndexedDB 有一个版本(version)的概念,连接数据库时就可以指定版本
const version = 1;
const request = window.indexedDB.open('pku', version);
版本主要用来控制数据库的结构,当数据库结构(表结构)发生变化时,版本也会变化
如上,request
上有四个事件:
onupgradeneeded
在版本改变时触发
- 注意首次连接数据库时,版本从 0 变成 1,因此也会触发,且先于
onsuccess
onsuccess
在连接成功后触发onerror
在连接失败时触发onblocked
在连接被阻止的时候触发,比如打开版本低于当前存在的版本
注意这四个事件都是异步的,意味着在连接 IndexedDB 的请求发出去后,需要过一段时间才能连上数据库,并进行操作
开发者对数据库的所有操作,都得放在异步连上数据库之后,这有的时候会带来很大的不便
而开发者如果想创建数据表(在 IndexedDB 里面叫做 ObjectStore),只能将其放到 onupgradeneeded
事件中(官方的定义是需要一个 IDBVersionChange
的事件)
request.onupgradeneeded = function (event) {
db = event.target.result;
if (!db.objectStoreNames.contains('student')) {
db.createObjectStore('student', {
keyPath: 'id', // 主键
autoIncrement: true // 自增
});
}
}
上面这段代码,在数据库初始化时,创建了一个 student
的表,并且以 id
为自增主键(每加一条数据,主键会自动增长,无需开发者指定)
在这一切做好以后,终于,我们可以连接数据库,然后添加数据了
const adding = db.transaction('student', 'readwrite') // 创建事务
.objectStore('student') // 指定 student 表
.add({ name: 'luke', age: 22 });
adding.onsuccess = function (event) {
console.log('write success');
};
adding.onerror = function (event) {
console.log('write failed');
}
用同样的方法再加一条数据
db.transaction('student', 'readwrite')
.objectStore('student')
.add({ name: 'elaine', age: 23 });
然后,打开浏览器的开发者工具,我们就能看到添加的数据:
这个 key-value 结构对应的 Table 结构如下:
如果要获取数据,需要一个 readonly
的 Transaction
const request = db.transaction('student', 'readonly')
.objectStore(this.name)
.get(2); // 获取 id 为 2 的数据
request.onsuccess = function (event) {
console.log(event.target.result) // { id: 2, name: 'elaine', age: 23 }
}
综上,哪怕只是想简单的往 IndexedDB 里增加和查询数据,都需要写一大堆代码,操作非常繁琐,一不小心还容易掉坑里
这就是 GoDB 的设计初衷:将 IndexedDB 的操作大幅简化,在背后帮你把麻烦事解决
← 前言 Hello World →