Web SQL
首先呢,HTML5已经放弃了 Web SQL,这里做这个封装,主要是为了练手,另外是为了做在线演示。
后端一般都采用关系型数据库,有些关于sql语句的在线演示就需要弄个后端,但是成本比较高,那么如果能够用前端的websql模仿一下,是不是可以呢?
因为都是用sql语句,基础的操作方式还是相同的,介绍一下原理没啥问题,所以呢,并不是真的要在项目里面使用,对于websql目前初步的感受来说,挺麻烦的。。。
打开数据库
要说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实在太不放心了,维护起来也感觉忒麻烦。
可以删除表,但是听说不能删除数据库?那难道只能等360清理?