首先,webSQL并不在HTML的标准内,这里封装 webSQL 主要是为了后端的 MySQL。
因为想在前端做一个缓存机制,实现一些类似于 git 的功能,这个以后在细说,总之,想按照后端封装MySQL的方式,封装一下webSQL,实现接口、参数、返回值基本一致,或者类似。
SQL 的操作都很基础,核心就是两件事情:打开连接,传递SQL,得到结果。
我们先封装一个help。
/**
* 基于 promise 封装 webSQL 的基本操作
* * 创建数据库连接
* * 提交SQL给数据库执行,得到返回结果
* * 共用函数
* * info 结构:
* * * dbName: 'test', // 数据库名称
* * * ver: '1', // 版本,很重要
* * * size: '2', // 大小,自动 * 1024 * 1024
* * * description: '数据库描述'
*/
export default class MySQLHelp {
constructor (info) {
// 创建普通连接的参数
this._info = {
dbName: info.dbName,
ver: info.ver,
size: info.size,
description: info.infodescription
}
// 打开数据库
this.db = window.openDatabase(
this._info.dbName,
this._info.ver,
this._info.description,
this._info.size * 1024 * 1024)
console.log(` ★ 初始化!${info.dbName}`)
}
/**
* 开启一个事务,Promise 的方式
* @returns Promise 形式
*/
begin() {
const myPromise = new Promise((resolve, reject) => {
// 开启一个事务。
console.log('★ 开启事务,promise 模式')
this.db.transaction(
(tx) => { resolve(tx) },
(tx, err) => {reject(err)}
)
})
return myPromise
}
/**
* 直接运行SQL
* @param { string } sql SQL语句
* @param { arror } param 参数
* @returns promise
*/
query (sql, param=[], cn = null) {
const promise1 = new Promise((resolve, reject) => {
const _query = (_cn) => {
_cn.executeSql(
sql, param,
(tx, results) => { resolve(results) },
(tx, err) => {
console.log('query - sql:', sql, param, tx, err)
reject(err)
return true // 回滚
}
)
}
if (cn === null) {
this.begin().then((__cn) => {
_query(__cn)
})
} else {
_query(cn)
}
})
return promise1
}
}
window.openDatabase
创建并且打开一个数据库begin() db.transaction
开启一个事务。似乎好像必须先开始事务,然后才可以访问数据库。所以把这个拿出来单独封装一个函数,主要是为了能够连续使用事务。query(sql, param=[], cn = null)
把 SQL 提交给数据库执行(含参数),然后等待返回结果。
这里写的有点绕,主要是想兼容是否使用事务的两种用法。
使用方式
// 测试webSQL的访问
import webSQL from '../../packages/nf-ws-indexeddb/help.js'
const info = {
dbName: 'test_websqldb',
ver: '1',
size: 2,
description: '测试一下新的封装方式'
}
const help = new webSQL(info)
const tableName = 't_table'
const cols = ['a','b']
const sql = `CREATE TABLE IF NOT EXISTS ${tableName} (ID INTEGER PRIMARY KEY ASC, ${cols.join(',')} )`
help.query(sql, []).then((res) => {
console.log('建表完毕', res)
// const sql2 = 'insert into t_table (id,a,b) values (0,?,?)'
const sql2 = 'insert into t_table (a,b) values (?,?)'
const param = ['111','ddd']
help.query(sql2, param).then((res) => {
console.log('添加数据:', res)
})
})
建立对象
首先 import 进来,然 new 一个对象出来。建表
后端的MySQL可以在上线前先把数据库准备好,但是前端的webSQL就得每次运行前先检查是否已经有表,所以第一步是建立一个表。
webSQL 比较宽松,似乎没有对字段类型进行检查,所以建表的时候也就不去定义字段类型了。添加数据
再做一个添加数据的测试。
其他用法
- 连续用法
const tableName = 't_table'
const cols = ['a','b']
const sql = `CREATE TABLE IF NOT EXISTS ${tableName} (ID INTEGER PRIMARY KEY ASC, ${cols.join(',')} )`
help.query(sql, []).then((res) => {
console.log('建表完毕', res)
})
const sql2 = 'insert into t_table (a,b) values (?,?)'
const param = ['222','eee']
help.query(sql2, param).then((res) => {
console.log('添加数据:', res)
})
因为 js 是单线程的,所以其实我们可以依次写代码,而不必把下一条放在回调函数里面。
其实这种用法开启了两次事务。
- 事务用法
help.begin().then((cn) => {
help.query(sql, [], cn).then((res) => {
console.log('建表完毕', res)
})
help.query(sql2, param, cn).then((res) => {
console.log('添加数据:', res)
})
})
我们可以显性开始事务,这样就不必多次开启事务了。
不用提交事务?
事务好像是默认提交的。
如果要回滚事务的话,需要第四个参数(最后一个回调函数) return true。
两个事务同时写会如何?
// 事务一
help.begin().then((cn) => {
param[1] = '11'
help.query(sql2, param, cn).then((res) => {
console.log('添加数据:', res)
})
param[1] = '22'
help.query(sql2, param, cn).then((res) => {
console.log('添加数据:', res)
})
param[1] = '33'
help.query('', param, cn).then((res) => { // 触发回滚
console.log('添加数据:', res)
})
})
// 事务二
help.begin().then((cn) => {
param[1] = '66'
help.query(sql2, param, cn).then((res) => {
console.log('添加数据:', res)
})
param[1] = '77'
help.query(sql2, param, cn).then((res) => {
console.log('添加数据:', res)
})
param[1] = '88'
help.query(sql2, param, cn).then((res) => {
console.log('添加数据:', res)
})
})
测试了一下,感觉和后端的 node + MySQL 不太一样。
前端并没有出现交替添加的情况,而是依次添加。可能是因为,都是单线程的原因吧。
事务也可以独立回滚,不会影响另一个事务。
就这?
当然不是,这只是最基础的操作,后面更精彩。。。