欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

JS浏览器数据库

程序员文章站 2022-06-13 10:03:20
...

最近做功能需要浏览器自己备份一份数据,但是本地数据还需要支持条件查询。

于是,查找了一下是否有浏览器数据库这种东西。发现了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中封装了许多查询接口,可以在官方的介绍中看到:

https://github.com/coresmart/persistencejs