Web SQL
首先呢,HTML5已经放弃了 Web SQL,这里做这个封装,主要是为了练手,另外是为了做在线演示。
后端一般都采用关系型数据库,有些关于sql语句的在线演示就需要弄个后端,但是成本比较高,那么如果能够用前端的websql模仿一下,是不是就省钱了呢?
因为都是用sql语句,基础的操作方式还是相同的,介绍一下原理是没啥问题的,所以呢,并不是真的要在项目里面使用,目前初步的感受来说,挺麻烦的。。。
打开数据库
要说webSQL还真是简单,或者说是简陋,就是开库,事务,提交SQL语句,然后就没了,自由发挥吧。
那么还是简单的封装一下。
/**
* webSQL 的封装,基于 promise 便于操作
* * 建立数据库
* * 封装sql实现增删改查
*/
export default class webSQLHelp {
constructor (dbName, ver, description) { // constructor是一个构造方法,用来接收参数
this.dbName = dbName // this代表的是实例对象
this.ver = ver
this.description = description
this.db = window.openDatabase(this.dbName, this.ver, this.description, 2 * 1024 * 1024)
}
/**
* 打开指定的webSQL数据库
* @returns 数据库的实例
*/
openDb () {
return this.db
}
...
}
先体验一下ES6 的 class(不是TS的),果然和 function 没啥大的区别,初始化传入参数打开数据库,备用。
好像把数据库名称记录下来似乎也没啥大用。
建立表
然后用建表语句建立表,看了一下资料,似乎也不用设置字段的类型,那么就简单一点,根据对象的属性来建立一个表。
/**
* 创建表
* @param { string } tableName 表名
* @param { object } columns 表的对象,比如{name:'jyk', age: 12}
* @returns 成功或者失败
*/
createTable (tableName, columns) {
const promise = new Promise((resolve, reject) => {
console.log('this.db', this.db)
// 记录字段名称,不设置类型了。
const cols = []
for (const key in columns) {
cols.push(key)
}
const sql = `CREATE TABLE IF NOT EXISTS ${tableName}
(ID INTEGER PRIMARY KEY ASC, ${cols.join(',')} )`
console.log('createSQL:', sql)
// 调用事务,建立表
this.db.transaction((tx) => {
tx.executeSql(sql, [], (tx, results) => {
console.log(tx, results)
resolve(results)
}, (tx, err) => {
console.log(tx, err)
reject(err)
})
})
})
return promise
}
用 Promise 代替回调的方式。传入表名和对象,然后创建建表的SQL,提交建表搞定。
这里想实现 那种 表名.insert()
的形式,但是水平有限,没弄出来。
添加数据
/**
* 插入数据
* @param { string } tableName 表名
* @param { object } object 保存的对象
* @returns 新增的ID值
*/
insert (tableName, object) {
const promise = new Promise((resolve, reject) => {
console.log('this.db', this.db)
// 记录字段名称
const colNames = []
// 记录字段对应的值
const colValues = []
// 记录字段对应的占位符合
const cols = []
// 变量对象,记录 key和 value
for (const key in object) {
colNames.push(key)
// colValues.push('"' + object[key] + '"')
colValues.push(object[key])
cols.push('?')
}
const sql = `INSERT INTO ${tableName}
( ${colNames.join(',')} ) VALUES ( ${cols.join(',')} )`
console.log('insertSQL:', sql)
this.db.transaction((tx) => {
tx.executeSql(sql, colValues, (tx, results) => {
console.log(tx, results)
// 成功了,返回给调用者
resolve(results.insertId)
}, (tx, err) => {
console.log(tx, err)
reject(err)
})
})
})
return promise
}
还是传入表名和对象,然后生成 insert 的SQL语句,提交添加搞定。
修改数据
/**
* 修改数据
* @param { String } tableName 表名
* @param { Object } object 要修改的对象值
* @param { Number } idValue 修改依据,id 的值
* @returns 修改影响的行数
*/
update (tableName, object, idValue) {
const promise = new Promise((resolve, reject) => {
console.log('this.db', this.db)
// 记录字段名称
const colNames = []
// 记录字段对应的值
const colValues = []
// 变量对象,记录 key和 value
for (const key in object) {
colNames.push(key + '=? ')
colValues.push(object[key])
}
// 加入查询条件
colValues.push(idValue)
const sql = `UPDATE ${tableName} SET ${colNames.join(',')} WHERE id=?`
console.log('updateSQL:', sql)
console.log('updateSQL2:', colValues)
this.db.transaction((tx) => {
tx.executeSql(sql, colValues, (tx, results) => {
console.log(tx, results)
// 成功了,返回给调用者 影响行数
resolve(results.rowsAffected)
}, (tx, err) => {
console.log(tx, err)
reject(err)
})
})
})
return promise
}
一样,改改SQL就好。另外需要加一个where 的条件,否则就都删掉了。
删除数据
/**
* 删除一条记录
* @param { String } tableName 表名
* @param { Number } idValue 删除依据
* @returns 删除状态
*/
delete (tableName, idValue) {
const promise = new Promise((resolve, reject) => {
console.log('this.db', this.db)
const sql = `DELETE FROM ${tableName} WHERE id=?`
console.log('deleteSQL:', sql)
this.db.transaction((tx) => {
tx.executeSql(sql, [idValue], (tx, results) => {
console.log(tx, results)
// 成功了,返回给调用者 影响行数
resolve(results.rowsAffected)
}, (tx, err) => {
console.log(tx, err)
reject(err)
})
})
})
return promise
}
这个最简单了,delete一下就好。
查询数据
/**
* 查询数据
* @param { string } tableName 要查询的表名
* @param { object } showCols 显示字段
* @param { object } query 查询条件
* @returns 查询结果,数组形式
*/
select (tableName, showCols, query) {
this.findKind = {
// 字符串
401: ' {col} = ? ',
402: ' {col} <> ? ',
403: ' {col} like ? ',
404: ' {col} not like ? ',
405: ' {col} like ? ', // 起始于
406: ' {col} like ? ', // 结束于
// 数字
411: ' {col} = ? ',
412: ' {col} <> ? ',
413: ' {col} > ? ',
414: ' {col} >= ? ',
415: ' {col} < ? ',
416: ' {col} <= ? ',
417: ' {col} between ? and ? ',
// 日期
421: ' {col} = ? ',
422: ' {col} <> ? ',
423: ' {col} > ? ',
424: ' {col} >= ? ',
425: ' {col} < ? ',
426: ' {col} <= ? ',
427: ' {col} between ? and ? ',
// 范围
441: ' {col} in (?)'
}
const promise = new Promise((resolve, reject) => {
const _whereCol = []
const _whereValue = []
for (const key in query) {
const val = query[key]
_whereCol.push(this.findKind[val[0]].replace('{col}', key))
switch (val[0]) {
case 403: // like
case 404: // not like
_whereValue.push('%' + val[1] + '%')
break
case 405: // like a%
_whereValue.push(val[1] + '%')
break
case 406: // like %a
_whereValue.push('%' + val[1])
break
case 417: // between 数字
case 427: // between 日期
_whereValue.push(...val[1])
break
case 441: // in
_whereCol[_whereCol.length - 1] =
_whereCol[_whereCol.length - 1]
.replace('?', val[1].map(a => '?').join(','))
_whereValue.push(...val[1])
break
default:
_whereValue.push(val[1])
break
}
}
if (_whereCol.length === 0) {
_whereCol.push(' 1=1 ')
}
const sql = `SELECT * FROM ${tableName} WHERE ${_whereCol.join(' and ')}`
console.log('selectSQL1:', sql)
console.log('selectSQL2:', _whereValue)
this.db.transaction((tx) => {
tx.executeSql(sql, _whereValue, (tx, results) => {
console.log(tx, results)
// 成功了,返回给调用者 影响行数
resolve(results.rows)
}, (tx, err) => {
console.log(tx, err)
reject(err)
})
})
})
return promise
}
好吧,其实主要就是为了这个查询,这不在做查询控件么,演示的时候,还是有个数据变化更生动一些,但是有没钱弄后端服务器。于是就用websql代替一下吧。
这里设置了一个查询结构,这个 select 就是把这个查询结构解析成SQL语句,然后交给数据库执行,看看查询效果。
使用方法
在vue环境下使用
import WebSqlHelp from '@/store-nf/websql-help.js'
export default {
name: 'websql',
components: {
},
setup (props, ctx) {
const help = new WebSqlHelp('db-findtest', '1.0', '演示一下查询控件')
console.log(help)
const person = {
name: 'jyk',
age: 18,
brithday: '1970-1-1',
aa: {
a1: 'a111',
a2: 'a222'
}
}
const sqlCreate = () => {
// 依据 person 建立一个表
help.createTable('person3', person).then(() => {
// 往表里面加数据
help.insert('person3', person).then((id) => {
console.log('newId', id)
})
})
}
const zsgc = () => {
// 修改指定的数据
person.age = 111
help.update('person3', person, 3).then((id) => {
console.log('updateId', id)
})
// 删除指定的数据
help.delete('person3', 4).then((id) => {
console.log('deleteId', id)
})
}
const dataList = reactive([])
const sqlSelect = () => {
// 查询数据
help.select('person3', {}, {
// id: [401, 2]
}).then((data) => {
console.log('select:', data)
dataList.length = 0
dataList.push(...data)
})
}
return {
sqlCreate,
sqlSelect,
dataList
}
}
}
嵌套对象
默认只支持单层属性,套娃是不支持的,直接存 “[object Object]”这个东东进去。
只是简单的演示,就不做转换了。多表关联
没考虑那么复杂,因为没打算演示这些复杂的操作,头疼。就简单粗暴的演示一下单表查询就好。
同理,分组统计这类的也不考虑。
总结
发现好多小问题,前端嘛,还是用indexedDB的好,用webSQL实在太不放心了,维护起来也感觉忒麻烦。
对了,还得写个删除表和库的函数,F12也没有个删除的功能。