当前位置: 首页 > news >正文

针对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()
})()

测试

查询数据

查询数据我们需要根据不同的查询方式来实现不同的方法

  1. 通过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('数据库未打开')}})
    }
    
  2. 查询全部数据记录

    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('数据库未打开')}})
    }
    
  3. 通过游标查询

    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('数据库未打开')}})
    }
    
  4. 通过指定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.代理配置 总结 前言 前后端项目分离衍生出浏览器跨域问题,开发之前我们通过配置代理解决这个问题。 一、浏览器跨域 浏览器的跨域问题主要是由于浏览器的同源策略导致的。同源策略是浏览器的一个安全功能&…...

基于FPGA的PID算法学习———实现PID比例控制算法

基于FPGA的PID算法学习 前言一、PID算法分析二、PID仿真分析1. PID代码2.PI代码3.P代码4.顶层5.测试文件6.仿真波形 总结 前言 学习内容:参考网站: PID算法控制 PID即:Proportional(比例)、Integral(积分&…...

Qt/C++开发监控GB28181系统/取流协议/同时支持udp/tcp被动/tcp主动

一、前言说明 在2011版本的gb28181协议中,拉取视频流只要求udp方式,从2016开始要求新增支持tcp被动和tcp主动两种方式,udp理论上会丢包的,所以实际使用过程可能会出现画面花屏的情况,而tcp肯定不丢包,起码…...

阿里云ACP云计算备考笔记 (5)——弹性伸缩

目录 第一章 概述 第二章 弹性伸缩简介 1、弹性伸缩 2、垂直伸缩 3、优势 4、应用场景 ① 无规律的业务量波动 ② 有规律的业务量波动 ③ 无明显业务量波动 ④ 混合型业务 ⑤ 消息通知 ⑥ 生命周期挂钩 ⑦ 自定义方式 ⑧ 滚的升级 5、使用限制 第三章 主要定义 …...

UE5 学习系列(三)创建和移动物体

这篇博客是该系列的第三篇,是在之前两篇博客的基础上展开,主要介绍如何在操作界面中创建和拖动物体,这篇博客跟随的视频链接如下: B 站视频:s03-创建和移动物体 如果你不打算开之前的博客并且对UE5 比较熟的话按照以…...

【大模型RAG】Docker 一键部署 Milvus 完整攻略

本文概要 Milvus 2.5 Stand-alone 版可通过 Docker 在几分钟内完成安装;只需暴露 19530(gRPC)与 9091(HTTP/WebUI)两个端口,即可让本地电脑通过 PyMilvus 或浏览器访问远程 Linux 服务器上的 Milvus。下面…...

k8s业务程序联调工具-KtConnect

概述 原理 工具作用是建立了一个从本地到集群的单向VPN,根据VPN原理,打通两个内网必然需要借助一个公共中继节点,ktconnect工具巧妙的利用k8s原生的portforward能力,简化了建立连接的过程,apiserver间接起到了中继节…...

docker 部署发现spring.profiles.active 问题

报错: org.springframework.boot.context.config.InvalidConfigDataPropertyException: Property spring.profiles.active imported from location class path resource [application-test.yml] is invalid in a profile specific resource [origin: class path re…...

Fabric V2.5 通用溯源系统——增加图片上传与下载功能

fabric-trace项目在发布一年后,部署量已突破1000次,为支持更多场景,现新增支持图片信息上链,本文对图片上传、下载功能代码进行梳理,包含智能合约、后端、前端部分。 一、智能合约修改 为了增加图片信息上链溯源,需要对底层数据结构进行修改,在此对智能合约中的农产品数…...

AGain DB和倍数增益的关系

我在设置一款索尼CMOS芯片时,Again增益0db变化为6DB,画面的变化只有2倍DN的增益,比如10变为20。 这与dB和线性增益的关系以及传感器处理流程有关。以下是具体原因分析: 1. dB与线性增益的换算关系 6dB对应的理论线性增益应为&…...

IP如何挑?2025年海外专线IP如何购买?

你花了时间和预算买了IP,结果IP质量不佳,项目效率低下不说,还可能带来莫名的网络问题,是不是太闹心了?尤其是在面对海外专线IP时,到底怎么才能买到适合自己的呢?所以,挑IP绝对是个技…...