JS浏览器数据库
最近做功能需要浏览器自己备份一份数据,但是本地数据还需要支持条件查询。
于是,查找了一下是否有浏览器数据库这种东西。发现了Web SQL和IndexedDB这两个东西,前者是关系型数据库,后者是NoSQL类型的。因为功能需要关系型查询,所以准备选用前者,但是前者兼容性有问题,而且不是标准的规范,而且停止了更新。
实践了一下发现Web SQL在火狐等浏览器上确实是不支持的,但是Chrome、Safari是支持的。本来想对于支持的使用Web SQL去做,不支持的使用IndexedDB采用另一台存储方案。不过在无意中发现了persistencejs。
persistencejs有好多套SQL的方案。对于浏览器提供了Web SQL存储和内存存储(支持导入到LocalStorage)两种方案。
所以对于支持Web SQL的,采用persistencejs的WebSQL方案。
对于不支持Web SQL的,采用内存方案,并将数据持久化到LocalStorage。
但是因为内存方案会比WebSQL方案多一个从LocalStorage读取数据和把输入存入LocalStorage两个步骤。
所以为了统一接口,我添加了dataInit和save接口。
dataInit对于WebSQL方案不会做任何事情,但是对于内存方案会执行loadFromLocalStorage读取数据。
save接口将替代flush接口,但是唯一不同的是内存方案会再执行完flush以后,执行saveToLocalStorage。
但是考虑到频繁saveToLocalStorage会影响性能,此处采用了我上一篇写的‘JS中实现函数在指定时间内单次执行’,实现在2秒内只会持久化一次。
基本上实现后封装成PersistenceSQL如下:(项目采用ReactJS编写)
import WebSQL from './WebSQL';
import Tools from '../Tools';
import persistence_meta from './persistencejs/persistence'
import {defaultTypeMapper, config} from './persistencejs/persistence.store.sql'
import websql_meta from './persistencejs/persistence.store.websql'
import memorysql_meta from './persistencejs/persistence.store.memory'
class PersistenceSQL {
constructor() {
this._sql_arg = {
name: 'persistencedb',
description: 'the persistence database',
maxSize: 5*1024*1024, // 字节
}
this.sql_init = false
this.persistence = persistence_meta.createPersistence()
this.persistence.store = this.persistence.store || {};
this.persistence.store.sql = {
defaultTypeMapper: defaultTypeMapper,
config: config
};
this.init()
}
static instance() {
if (Tools.isNone(PersistenceSQL.persistenceSQL)) {
PersistenceSQL.persistenceSQL = new PersistenceSQL()
}
return PersistenceSQL.persistenceSQL.persistence
}
initWebSQL() {
this.persistence.store.websql = {};
this.persistence.store.websql.config = websql_meta.persistence.store.websql.config
let arg = this._sql_arg
this.persistence.store.websql.config(this.persistence, arg.name, arg.description, arg.maxSize);
this.persistence.dataInit = (call_back)=>{
if (!Tools.isNone(call_back)) {
call_back()
}
}
this.persistence.save = this.persistence.flush
}
initMemorySQL() {
this.persistence.store.memory = {}
this.persistence.store.memory.config = memorysql_meta.config
let arg = this._sql_arg
this.persistence.store.memory.config(this.persistence, arg.name);
this.persistence.dataInit = this.persistence.loadFromLocalStorage
let saveToLocalStorage = Tools.exeOnceAtTime(()=>{
this.persistence.saveToLocalStorage()
}, 2000)
this.persistence.save = ()=>{
this.persistence.flush()
saveToLocalStorage()
}
}
init() {
if (WebSQL.judgeSupport()) {
try {
this.initWebSQL()
}
catch (e) {
print(e)
this.initMemorySQL()
}
}
else {
this.initMemorySQL()
}
}
}
export default PersistenceSQL
对于'./persistencejs/persistence'、 './persistencejs/persistence.store.sql'、'./persistencejs/persistence.store.websql'、'./persistencejs/persistence.store.memory'这四个文件,大部分网上的代码都是通过在html中标签引入的。此处采用CommonJS的方式进行了使用。
然后对数据库进行使用:
表结构定义:(第一次定义完以后,第二次执行这段代码将使用原先创建的表,不会再次执行创建表)
let persistence = PersistenceSQL.instance()
this.test_table = persistence.define('test', {
test_id: 'INT',
xxxx_id: 'INT',
day_time_stamp: 'INT',
type: 'INT',
user_name: 'TEXT',
content: 'JSON',
});
this.test_table.index(['test_id', 'day_time_stamp', 'xxxx_id'], {unique: true})
persistence.schemaSync()
persistence.dataInit()
数据的存入:
let persistence = PersistenceSQL.instance()
let TestTable = this.test_table
for (let i = 0; i < args.length; i++) {
let arg = args[i]
let record = new TestTable({
test_id: arg.test_id,
xxxx_id: xxxx_id,
day_time_stamp: Tools.getNowDayTimeStamp(arg.time_stamp),
type: arg.type,
user_name: arg.user_name,
content: arg.content,
})
persistence.add(record)
}
persistence.save()
数据查询:
let persistence = PersistenceSQL.instance()
let TestTable = this.test_table
let query1 = new persistence.AndFilter(
new persistence.PropertyFilter('test_id', '=', test_id),
new persistence.PropertyFilter('day_time_stamp', '=', time_stamp)
)
if (id_from > 0) {
query1 = new persistence.AndFilter(
query1,
new persistence.PropertyFilter('test_id', '<', id_from)
)
}
let query2 = new persistence.AndFilter(
new persistence.PropertyFilter('test_id', '=', test_id),
new persistence.PropertyFilter('day_time_stamp', '<', time_stamp)
)
let query = TestTable.all().and(query1).or(query2).order("day_time_stamp", false).order('test_id', false).limit(message_count);
query.list(null, (data)=>{
let ans = []
if (data.length == 0) {
call_back(ans)
return
}
data.forEach((item)=>{
print(item.content)
ans.push(item)
})
call_back(ans)
})
对于查询persistencejs中封装了许多查询接口,可以在官方的介绍中看到: