针对indexedDB的简易封装
连接数据库
我们首先创建一个DBManager类,通过这个类new出来的对象管理一个数据库
具体关于indexedDB的相关内容可以看我的这篇博客
indexedDB
class DBManager{}
我们首先需要打开数据库,打开数据库需要数据库名和该数据库的版本
constructor(dbName, version) {this.dbName = dbName;this.version = version;this.db = null
}
在constructor中我们先初始化数据库相关信息,dbName为该对象管理的数据库的数据库名,version为该数据库的版本,db为该数据库的IDBDatabase对象
现在我们开始实现openDB方法
openDB() {return new Promise((resolve, reject) => {const cmd = indexedDB.open(this.dbName, this.version)cmd.onsuccess = (event) => {console.log('数据库打开成功')this.db = event.target.resultresolve(this.db)}cmd.onerror = (event) => {console.log('数据库打开失败')reject(event.target.error)}})
}
因为打开数据库涉及i/o操作,所以是异步的,所以我们需要返回一个Promise
关闭数据库
当数据库使用完毕,为了节省资源,我们可以选择断开数据库的连接
closeDB() {if (this.db) {console.log('关闭数据库')this.db.close()this.db = null}
}
删除数据库
如果数据库某一天不在使用,我们可以选择删除这个数据库来节省资源
deleteDB() {return new Promise((resolve, reject) => {const cmd = indexedDB.deleteDatabase(this.dbName)cmd.onsuccess = (event) => {console.log('数据库删除成功')resolve()}cmd.onerror = (event) => {console.log('数据库删除失败')reject(event.target.error)}})
}
同样,删除数据库是异步的,我们需要返回一个Promise
我们接下来来测试一下
(async function () {const db = new DBManager("student", 1)await db.openDB()await db.closeDB()await db.deleteDB()
})()

需要注意的是,我们在删除数据库之前必须先断开数据库连接
创建对象仓库
我们接下来需要实现创建对象的方法
createStore(storeName, keyPath, keys) {return new Promise((resolve, reject) => {if (this.db) {console.log('添加存储仓库', storeName)const store = this.db.createObjectStore(storeName, { keyPath: keyPath, autoIncrement: true })if (keys) {keys.forEach(key => {store.createIndex(key, key, { unique: key === keyPath ? true : false })})}resolve(this.db)} else {reject('数据库未打开')}})
}
但是如果我们直接通过调用createStore来创建对象仓库的话浏览器会报错

这是因为针对对象仓库的操作是需要放在db.onupgradeneeded的回调中,所以我们不能直接这么写
数据库的更新
我们可以用一个更新方法来手动触发onupgradeneeded这个事件
updateDB(callback) {return new Promise(async (resolve, reject) => {console.log('数据库升级')if (this.db) {this.closeDB()this.version += 1await this.openDB(callback)resolve(this.db)}else {reject('数据库未打开')}})
}
openDB(callback) {return new Promise((resolve, reject) => {const cmd = indexedDB.open(this.dbName, this.version)cmd.onsuccess = (event) => {console.log('数据库打开成功')this.db = event.target.resultresolve(this.db)}cmd.onerror = (event) => {console.log('数据库打开失败')reject(event.target.error)}if (callback) {cmd.onupgradeneeded = (event) => {this.db = event.target.resultcallback(event)}}})
}
update方法通过调用close和open方法更新数据库,同时将对对象仓库的操作封装成函数传入update方法中,再将这个函数放入open方法中,open方法中通过判断是否传入参数来判断是否需要监听onupgradeneeded事件,因为当用户第一次创建数据库的时候会触发这个事件,而第一次的时候我们是不需要监听的
接下来我们重新处理下createStore里的逻辑
createStore(storeName, keyPath, keys) {return new Promise(async (resolve, reject) => {if (this.db) {await this.updateDB((event) => {console.log('添加存储仓库', storeName)const store = this.db.createObjectStore(storeName, { keyPath: keyPath, autoIncrement: true })if (keys) {keys.forEach(key => {store.createIndex(key, key, { unique: key === keyPath ? true : false })})}})resolve(this.db)} else {reject('数据库未打开')}})
}
接下来我们再来测试一下
(async function () {const db = new DBManager("student", 1)await db.openDB()await db.createStore("student", "id", ['id', 'name', 'age', 'score'])await db.closeDB()await db.deleteDB()
})()

为什么是先打印添加存储仓库,后打印数据库打开?因为当IDBDatabase对象同时出发onsuccess和onupgradeneeded事件时,会先执行onupgradeneeded的回调,然后执行onsuccess中的回调
数据记录的操作
我们接下来实现关于数据记录的方法
增加数据
增加数据记录的逻辑较为简单,调用indexedDB提供的add方法就行
insert(storeName, data) {return new Promise((resolve, reject) => {if (this.db) {const transaction = this.db.transaction(storeName, 'readwrite')const store = transaction.objectStore(storeName)const cmd = store.add(data)cmd.onsuccess = (event) => {console.log('插入数据成功')resolve(event.target.result)}cmd.onerror = (event) => {console.log('插入数据失败')reject(event.target.error)}} else {reject('数据库未打开')}})
}
我们来测试一下
(async function () {const db = new DBManager("student", 1)await db.openDB()await db.createStore("student", "id", ['id', 'name', 'age', 'score'])await db.insert("student", { id: 1, name: "张三", age: 18, score: 90 })await db.insert("student", { id: 2, name: "李四", age: 20, score: 56 })await db.closeDB()await db.deleteDB()
})()

查询数据
查询数据我们需要根据不同的查询方式来实现不同的方法
-
通过key查询
queryByKey(storeName, value) {return new Promise((resolve, reject) => {if (this.db) {const transaction = this.db.transaction(storeName, 'readonly')const store = transaction.objectStore(storeName)const cmd = store.get(value)cmd.onsuccess = (event) => {console.log('查询数据成功')resolve(event.target.result)}cmd.onerror = (event) => {console.log('查询数据失败')reject(event.target.error)}} else {reject('数据库未打开')}}) } -
查询全部数据记录
queryAll(storeName) {return new Promise((resolve, reject) => {if (this.db) {const transaction = this.db.transaction(storeName, 'readonly')const store = transaction.objectStore(storeName)const cmd = store.getAll()cmd.onsuccess = (event) => {console.log('查询数据成功')resolve(event.target.result)}cmd.onerror = (event) => {console.log('查询数据失败')reject(event.target.error)}} else {reject('数据库未打开')}}) } -
通过游标查询
queryByCursor(storeName, range, direction = "next") {return new Promise((resolve, reject) => {if (this.db) {const transaction = this.db.transaction(storeName, 'readonly')const store = transaction.objectStore(storeName)const cursor = range ? store.openCursor(range, direction) : store.openCursor()const result = []cursor.onsuccess = (event) => {const cursor = event.target.resultif (cursor) {result.push(cursor.value)cursor.continue()} else {console.log('查询数据成功')resolve(result)}}cursor.onerror = (event) => {console.log('查询数据失败')reject(event.target.error)}} else {reject('数据库未打开')}}) } -
通过指定key-value查询
queryByIndex(storeName, indexName, value) {return new Promise((resolve, reject) => {if (this.db) {const transaction = this.db.transaction(storeName, 'readonly')const store = transaction.objectStore(storeName)const cmd = store.index(indexName).get(value)cmd.onsuccess = (event) => {console.log('查询数据成功')resolve(event.target.result)}cmd.onerror = (event) => {console.log('查询数据失败')reject(event.target.error)}} else {reject('数据库未打开')}}) }
我们现在来测试一下
(async function () {const db = new DBManager("student", 1)await db.openDB()await db.createStore("student", "id", ['id', 'name', 'age', 'score'])await db.insert("student", { id: 1, name: "张三", age: 18, score: 90 })await db.insert("student", { id: 2, name: "李四", age: 20, score: 56 })await db.insert("student", { id: 3, name: "王五", age: 19, score: 80 })await db.insert("student", { id: 4, name: "赵六", score: 70 })const result = await db.queryByIndex("student", "age", 18)console.log(result)const result2 = await db.queryByKey("student", 3)console.log(result2)const result3 = await db.queryByCursor("student")console.log(result3)const result4 = await db.queryByCursor("student", IDBKeyRange.only(4))console.log(result4)const result5 = await db.queryAll("student")console.log(result5)await db.closeDB()await db.deleteDB()
})()

更新数据
更新数据记录也是通过调用indexedDB中的put方法来实现
update(storeName, key, data) {return new Promise((resolve, reject) => {if (this.db) {const transaction = this.db.transaction(storeName, 'readwrite')const store = transaction.objectStore(storeName)const cmd = store.put(data)cmd.onsuccess = (event) => {console.log('更新数据成功')resolve(event.target.result)}cmd.onerror = (event) => {console.log('更新数据失败')reject(event.target.error)}} else {reject('数据库未打开')}})
}
删除数据
更新数据记录也是通过调用indexedDB中的delete方法来实现
delete(storeName, key) {return new Promise((resolve, reject) => {if (this.db) {const transaction = this.db.transaction(storeName, 'readwrite')const store = transaction.objectStore(storeName)const cmd = store.delete(key)cmd.onsuccess = (event) => {console.log('删除数据成功')resolve(event.target.result)}cmd.onerror = (event) => {console.log('删除数据失败')reject(event.target.error)}} else {reject('数据库未打开')}})
}
完整代码
最后我们来看一下完整代码
class DBManager {constructor(dbName, version) {this.dbName = dbName;this.version = version;this.db = null}openDB(callback) {return new Promise((resolve, reject) => {const cmd = indexedDB.open(this.dbName, this.version)cmd.onsuccess = (event) => {console.log('数据库打开成功')this.db = event.target.resultresolve(this.db)}cmd.onerror = (event) => {console.log('数据库打开失败')reject(event.target.error)}if (callback) {cmd.onupgradeneeded = (event) => {this.db = event.target.resultcallback(event)}}})}closeDB() {if (this.db) {console.log('关闭数据库')this.db.close()this.db = null}}deleteDB() {return new Promise((resolve, reject) => {const cmd = indexedDB.deleteDatabase(this.dbName)cmd.onsuccess = (event) => {console.log('数据库删除成功')resolve()}cmd.onerror = (event) => {console.log('数据库删除失败')reject(event.target.error)}})}updateDB(callback) {return new Promise(async (resolve, reject) => {console.log('数据库升级')if (this.db) {this.closeDB()this.version += 1await this.openDB(callback)resolve(this.db)}else {reject('数据库未打开')}})}createStore(storeName, keyPath, keys) {return new Promise(async (resolve, reject) => {if (this.db) {await this.updateDB((event) => {console.log('添加存储仓库', storeName)const store = this.db.createObjectStore(storeName, { keyPath: keyPath, autoIncrement: true })if (keys) {keys.forEach(key => {store.createIndex(key, key, { unique: key === keyPath ? true : false })})}})resolve(this.db)} else {reject('数据库未打开')}})}deleteStore(storeName) {return new Promise(async (resolve, reject) => {if (this.db) {await this.updateDB((event) => {console.log('删除存储仓库', storeName)const store = this.db.deleteObjectStore(storeName)})resolve(this.db)} else {reject('数据库未打开')}})}insert(storeName, data) {return new Promise((resolve, reject) => {if (this.db) {const transaction = this.db.transaction(storeName, 'readwrite')const store = transaction.objectStore(storeName)const cmd = store.add(data)cmd.onsuccess = (event) => {console.log('插入数据成功')resolve(event.target.result)}cmd.onerror = (event) => {console.log('插入数据失败')reject(event.target.error)}} else {reject('数据库未打开')}})}update(storeName, key, data) {return new Promise((resolve, reject) => {if (this.db) {const transaction = this.db.transaction(storeName, 'readwrite')const store = transaction.objectStore(storeName)const cmd = store.put(data)cmd.onsuccess = (event) => {console.log('更新数据成功')resolve(event.target.result)}cmd.onerror = (event) => {console.log('更新数据失败')reject(event.target.error)}} else {reject('数据库未打开')}})}delete(storeName, key) {return new Promise((resolve, reject) => {if (this.db) {const transaction = this.db.transaction(storeName, 'readwrite')const store = transaction.objectStore(storeName)const cmd = store.delete(key)cmd.onsuccess = (event) => {console.log('删除数据成功')resolve(event.target.result)}cmd.onerror = (event) => {console.log('删除数据失败')reject(event.target.error)}} else {reject('数据库未打开')}})}queryByKey(storeName, value) {return new Promise((resolve, reject) => {if (this.db) {const transaction = this.db.transaction(storeName, 'readonly')const store = transaction.objectStore(storeName)const cmd = store.get(value)cmd.onsuccess = (event) => {console.log('查询数据成功')resolve(event.target.result)}cmd.onerror = (event) => {console.log('查询数据失败')reject(event.target.error)}} else {reject('数据库未打开')}})}queryAll(storeName) {return new Promise((resolve, reject) => {if (this.db) {const transaction = this.db.transaction(storeName, 'readonly')const store = transaction.objectStore(storeName)const cmd = store.getAll()cmd.onsuccess = (event) => {console.log('查询数据成功')resolve(event.target.result)}cmd.onerror = (event) => {console.log('查询数据失败')reject(event.target.error)}} else {reject('数据库未打开')}})}queryByIndex(storeName, indexName, value) {return new Promise((resolve, reject) => {if (this.db) {const transaction = this.db.transaction(storeName, 'readonly')const store = transaction.objectStore(storeName)const cmd = store.index(indexName).get(value)cmd.onsuccess = (event) => {console.log('查询数据成功')resolve(event.target.result)}cmd.onerror = (event) => {console.log('查询数据失败')reject(event.target.error)}} else {reject('数据库未打开')}})}queryByCursor(storeName, range, direction = "next") {return new Promise((resolve, reject) => {if (this.db) {const transaction = this.db.transaction(storeName, 'readonly')const store = transaction.objectStore(storeName)const cursor = range ? store.openCursor(range, direction) : store.openCursor()const result = []cursor.onsuccess = (event) => {const cursor = event.target.resultif (cursor) {result.push(cursor.value)cursor.continue()} else {console.log('查询数据成功')resolve(result)}}cursor.onerror = (event) => {console.log('查询数据失败')reject(event.target.error)}} else {reject('数据库未打开')}})}
}
相关文章:
针对indexedDB的简易封装
连接数据库 我们首先创建一个DBManager类,通过这个类new出来的对象管理一个数据库 具体关于indexedDB的相关内容可以看我的这篇博客 indexedDB class DBManager{}我们首先需要打开数据库,打开数据库需要数据库名和该数据库的版本 constructor(dbName,…...
网络编程--网络理论基础(二)
这里写目录标题 网络通信流程mac地址、ip地址arp协议交换机路由器简介子网划分网关 路由总结 为什么ip相同的主机在与同一个互联网服务通信时不冲突公网ip对于同一个路由器下的不同设备,虽然ip不冲突,但是因为都是由路由器的公网ip转发通信,接…...
Python MongoDB 基本操作
本文内容主要为使用Python 对Mongodb数据库的一些基本操作整理。 目录 安装类库 操作实例 引用类库 连接服务器 连接数据库 添加文档 添加单条 批量添加 查询文档 查询所有文档 查询部分文档 使用id查询 统计查询 排序 分页查询 更新文档 update_one方法 upd…...
Node.js 入门:
Node.js 是一个开源、跨平台的 JavaScript 运行时环境,它允许开发者在浏览器之外编写命令行工具和服务器端脚本。以下是一些关于 Node.js 的基础教程: 1. **Node.js 入门**: - 了解 Node.js 的基本概念,包括它是一个基于 Chro…...
java8 List的Stream流操作 (实用篇 三)
目录 java8 List的Stream流操作 (实用篇 三) 初始数据 1、Stream过滤: 过滤-常用方法 1.1 筛选单元素--年龄等于18 1.2 筛选单元素--年龄大于18 1.3 筛选范围--年龄大于18 and 年龄小于40 1.4 多条件筛选--年龄大于18 or 年龄小于40 and sex男 1.5 多条件筛…...
机器学习python实践——数据“相关性“的一些补充性个人思考
在上一篇“数据白化”的文章中,说到了数据“相关性”的概念,但是在统计学中,不仅存在“相关性”还存在“独立性”等等,所以,本文主要对数据“相关性”进行一些补充。当然,如果这篇文章还能入得了各位“看官…...
MySQL——触发器(trigger)基本结构
1、修改分隔符符号 delimiter $$ $$可以修改 2、创建触发器函数名称 create trigger 函数名 3、什么样在操作触发,操作哪个表 after :……之后触发 before :……之后触发 insert :……之后触发 update :……之后触…...
数字孪生定义及应用介绍
数字孪生定义及应用介绍 1 数字孪生(Digital Twin, DT)概述1.1 定义1.2 功能1.3 使用场景1.4 数字孪生三步走1.4.1 数字模型1.4.2 数字影子1.4.3 数字孪生 数字孪生地球平台Earth-2 参考 1 数字孪生(Digital Twin, DT)概述 数字孪…...
数据赋能(122)——体系:数据清洗——技术方法、主要工具
技术方法 数据清洗标准模型是将数据输入到数据清洗处理器,通过一系列步骤“清理”数据,然后以期望的格式输出清理过的数据。数据清洗从数据的准确性、完整性、一致性、惟一性、适时性、有效性几个方面来处理数据的丢失值、越界值、不一致代码、重复数据…...
【SCAU数据挖掘】数据挖掘期末总复习题库简答题及解析——中
1. 某学校对入学的新生进行性格问卷调查(没有心理学家的参与),根据学生对问题的回答,把学生的性格分成了8个类别。请说明该数据挖掘任务是属于分类任务还是聚类任务?为什么?并利用该例说明聚类分析和分类分析的异同点。 解答: (a)该数据…...
2024年注册安全工程师报名常见问题汇总!
注册安全工程师报名 24年注册安全工程师报名已正式拉开序幕,报名时间为6月18日—7月10日,考试时间为10月26日—10月27日。 目前经有12个地区公布了2024年注册安全工程师报名时间: 注册安全工程师报名信息完善 根据注安报名系统提示&am…...
JRebel-JVMTI [FATAL] Couldn‘t write to C:\Users\中文用户名-完美解决
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 热部署下载参考博客解决第一步第二步第三步:第四步: 热部署下载 下载后启动报错:JRebel-JVMTI [FATAL] Couldn’t write to C:\…...
STM32基于DMA数据转运和AD多通道
文章目录 1. DMA数据转运 1.1 初始化DMA步骤 1.2 DMA的库函数 1.3 设置当前数据寄存器 1.4 DMA获取当前数据寄存器 2. DMA数据转运 2.1 DMA.C 2.2 DMA.H 2.3 MAIN.C 3. DMAAD多通道 3.1 AD.C 3.2 AD.H 3.3 MAIN.C 1. DMA数据转运 对于DMA的详细解析可以看下面这篇…...
安卓应用开发——Android Studio中通过id进行约束布局
在Android开发中,布局通常使用XML文件来描述,而约束(如相对位置、大小等)可以通过多种方式实现,但直接使用ID进行约束并不直接对应于Android的传统布局系统(如LinearLayout、RelativeLayout等)。…...
Elasticsearch过滤器(filter):原理及使用
Hi~!这里是奋斗的小羊,很荣幸您能阅读我的文章,诚请评论指点,欢迎欢迎 ~~ 💥💥个人主页:奋斗的小羊 💥💥所属专栏:C语言 🚀本系列文章为个人学习…...
Docker配置与使用详解
一、引言 随着云计算和微服务的兴起,Docker作为一种轻量级的容器化技术,越来越受到开发者和运维人员的青睐。Docker通过容器化的方式,将应用程序及其依赖项打包成一个可移植的镜像,从而实现了应用程序的快速部署和扩展。本文将详…...
触控MCU芯片(1):英飞凌PSoC第6代第7代
前言: 说到触摸MCU芯片,这个历史也是很久了,比如日常经常接触到的洗衣机、电冰箱、小家电,隔着一层玻璃,轻轻一按就能识别按键,感觉比过去纯机械式的按键更高级更美观,不仅白电,现在很多汽车也都在进行触摸按键的改版,不再使用笨重的机械按键,比如空调调温按键、档位…...
git pull报错:unable to pull from remote repository due to conflicting tag(s)
背景 我在vscode里正常拉取代码,突然就报了如题所示的错误。 原因 因为vscode的拉取按钮执行的实际命令是:git pull --tags origin branch-name,该命令的实际含义是从远程仓库拉取指定的分支和该远程仓库上的所有标签。 在拉取标签时本地的…...
Python将字符串用特定字符分割并前面加序号
Python将字符串用特定字符分割并前面加序号 Python将字符串用特定字符分割并前面加序号,今天项目中就遇到,看着不难,得花点时间搞出来急用啊,在网上找了一圈,没发现有完整流程的文章。所以就搞出来并写了这个文章。仅…...
【第16章】Vue实战篇之跨域解决
文章目录 前言一、浏览器跨域二、配置代理1.公共请求2.代理配置 总结 前言 前后端项目分离衍生出浏览器跨域问题,开发之前我们通过配置代理解决这个问题。 一、浏览器跨域 浏览器的跨域问题主要是由于浏览器的同源策略导致的。同源策略是浏览器的一个安全功能&…...
云计算——弹性云计算器(ECS)
弹性云服务器:ECS 概述 云计算重构了ICT系统,云计算平台厂商推出使得厂家能够主要关注应用管理而非平台管理的云平台,包含如下主要概念。 ECS(Elastic Cloud Server):即弹性云服务器,是云计算…...
8k长序列建模,蛋白质语言模型Prot42仅利用目标蛋白序列即可生成高亲和力结合剂
蛋白质结合剂(如抗体、抑制肽)在疾病诊断、成像分析及靶向药物递送等关键场景中发挥着不可替代的作用。传统上,高特异性蛋白质结合剂的开发高度依赖噬菌体展示、定向进化等实验技术,但这类方法普遍面临资源消耗巨大、研发周期冗长…...
深入理解JavaScript设计模式之单例模式
目录 什么是单例模式为什么需要单例模式常见应用场景包括 单例模式实现透明单例模式实现不透明单例模式用代理实现单例模式javaScript中的单例模式使用命名空间使用闭包封装私有变量 惰性单例通用的惰性单例 结语 什么是单例模式 单例模式(Singleton Pattern&#…...
sqlserver 根据指定字符 解析拼接字符串
DECLARE LotNo NVARCHAR(50)A,B,C DECLARE xml XML ( SELECT <x> REPLACE(LotNo, ,, </x><x>) </x> ) DECLARE ErrorCode NVARCHAR(50) -- 提取 XML 中的值 SELECT value x.value(., VARCHAR(MAX))…...
Unsafe Fileupload篇补充-木马的详细教程与木马分享(中国蚁剑方式)
在之前的皮卡丘靶场第九期Unsafe Fileupload篇中我们学习了木马的原理并且学了一个简单的木马文件 本期内容是为了更好的为大家解释木马(服务器方面的)的原理,连接,以及各种木马及连接工具的分享 文件木马:https://w…...
Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信
文章目录 Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信前言一、网络通信基础概念二、服务端与客户端的完整流程图解三、每一步的详细讲解和代码示例1. 创建Socket(服务端和客户端都要)2. 绑定本地地址和端口&#x…...
【7色560页】职场可视化逻辑图高级数据分析PPT模版
7种色调职场工作汇报PPT,橙蓝、黑红、红蓝、蓝橙灰、浅蓝、浅绿、深蓝七种色调模版 【7色560页】职场可视化逻辑图高级数据分析PPT模版:职场可视化逻辑图分析PPT模版https://pan.quark.cn/s/78aeabbd92d1...
return this;返回的是谁
一个审批系统的示例来演示责任链模式的实现。假设公司需要处理不同金额的采购申请,不同级别的经理有不同的审批权限: // 抽象处理者:审批者 abstract class Approver {protected Approver successor; // 下一个处理者// 设置下一个处理者pub…...
嵌入式学习笔记DAY33(网络编程——TCP)
一、网络架构 C/S (client/server 客户端/服务器):由客户端和服务器端两个部分组成。客户端通常是用户使用的应用程序,负责提供用户界面和交互逻辑 ,接收用户输入,向服务器发送请求,并展示服务…...
R语言速释制剂QBD解决方案之三
本文是《Quality by Design for ANDAs: An Example for Immediate-Release Dosage Forms》第一个处方的R语言解决方案。 第一个处方研究评估原料药粒径分布、MCC/Lactose比例、崩解剂用量对制剂CQAs的影响。 第二处方研究用于理解颗粒外加硬脂酸镁和滑石粉对片剂质量和可生产…...
