连接数据库
我们首先创建一个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.result
resolve(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 += 1
await 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.result
resolve(this.db)
}
cmd.onerror = (event) => {
console.log('数据库打开失败')
reject(event.target.error)
}
if (callback) {
cmd.onupgradeneeded = (event) => {
this.db = event.target.result
callback(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.result if (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.result
resolve(this.db)
}
cmd.onerror = (event) => {
console.log('数据库打开失败')
reject(event.target.error)
}
if (callback) {
cmd.onupgradeneeded = (event) => {
this.db = event.target.result
callback(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 += 1
await 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.result
if (cursor) {
result.push(cursor.value)
cursor.continue()
} else {
console.log('查询数据成功')
resolve(result)
}
}
cursor.onerror = (event) => {
console.log('查询数据失败')
reject(event.target.error)
}
} else {
reject('数据库未打开')
}
})
}
}