UNPKG

autojs-sqlite

Version:

autojs中二代api里esm环境下使用sqlite数据库的orm框架,风格参考sequelize

428 lines (427 loc) 12.1 kB
/* * @Author: 抠脚本人 * @QQ: 742374184 * @Date: 2022-12-15 18:09:37 * @LastEditTime: 2022-12-19 12:35:52 * @Description: 数据库模块 * 灵感来之不易,积累创造奇迹 */ import { install } from "rhino"; import path from "path"; import fs from "fs"; import EventEmitter from "events"; install(); export const DataTypes = { DATE: "DATETIME", DATEONLY: "DATE", TEXT: "TEXT", INTEGER: "INTEGER", BOOLEAN: "TINYINT(1)", }; const ContentValues = android.content.ContentValues; // 继承SQLiteOpenHelper,参见Android文档 https://developer.android.com/reference/android/database/sqlite/SQLiteOpenHelper const SQLHelper = await $java.defineClass( class SQLHelper extends android.database.sqlite.SQLiteOpenHelper { /** * 构造一个数据库文件 * @param {android.content.Context} context * @param {string} dbPath */ constructor(context, dbPath, version) { // 调用父类构造函数,指定数据库路径、版本等 super(context, dbPath, null, version); } /** * 重写父类方法,在数据库打开时执行创建表的语句 * @param {android.database.sqlite.SQLiteDatabase} db */ onCreate(db) { console.log("create", db); } /** * 更新数据库版本 * @param {android.database.sqlite.SQLiteDatabase} db * @param {number} oldVersion * @param {number} newVersion */ onUpgrade(db, oldVersion, newVersion) { console.log(`onUpgrade: ${oldVersion} to ${newVersion}`); } } ); class SQLiteHelper { static dataToContent(data) { const values = new ContentValues(); const arr = Object.entries(data); for (const kv of arr) { let targetValue; switch (typeof kv[1]) { case "number": if (Number.isInteger(kv[1])) { targetValue = $java.boxInt(kv[1]); } else { targetValue = $java.boxDouble(kv[1]); } break; case "string": targetValue = kv[1]; break; default: break; } values.put(kv[0], targetValue); } return values; } static toRawCreate(tableName, attributes) { let result = `CREATE TABLE IF NOT EXISTS ${tableName}(\n`; Object.entries(attributes).forEach(entry => { let value = entry[1]; if (typeof value === "string") { result += `'${entry[0]}' ` + value + ",\n"; } else { result += `'${entry[0]}' ` + value.type; if (value.primaryKey) { result += " PRIMARY KEY"; } if (value.autoIncrement) { result += " AUTOINCREMENT"; } if (value.notNull) { result += " NOT NULL"; } if (value.defaultValue) { result += ` DEFAULT ${value.defaultValue}`; } if (value.unique) { result += " UNIQUE"; } result += ",\n"; } }); return result.replace(/,\n$/, "\n)"); } static toSet(set) { let result = "set "; Object.entries(set).forEach(entry => { let value = entry[1]; if (typeof value === "string") { result += `${entry[0]}='${value}' , `; } else if (typeof value === "number") { result += `${entry[0]}=${value} , `; } else { let item = entry[0]; Object.entries(value).forEach(entry2 => { let target = entry2[1]; if (typeof target === "string") { target = `'${target}'`; } result += item + entry2[0] + target + " , "; }); } }); return result.replace(/ , $/, ""); } static toWhereClause(where) { let result = { clause: "", args: [], }; Object.entries(where).forEach(entry => { let value = entry[1]; if (typeof value === "string") { result.clause += `${entry[0]} = ? and `; result.args.push(value); } else if (typeof value === "number") { result.clause += `${entry[0]} = ? and `; result.args.push(String(value)); } else { let item = entry[0]; Object.entries(value).forEach(entry2 => { let target = entry2[1]; if (typeof target === "number") { target = String(target); } result.clause += item + " " + entry2[0] + " ? and "; result.args.push(target); }); } }); result.clause = result.clause.replace(/ and $/, ""); return result; } static toRawWhere(where) { let result = "where "; Object.entries(where).forEach(entry => { let value = entry[1]; if (typeof value === "string") { result += `${entry[0]}='${value}' and `; } else if (typeof value === "number") { result += `${entry[0]}=${value} and `; } else { let item = entry[0]; Object.entries(value).forEach(entry2 => { let target = entry2[1]; if (typeof target === "string") { target = `'${target}'`; } result += item + entry2[0] + target + " and "; }); } }); return result.replace(/ and $/, ""); } /** * 将查询结果转为数组 * @param {android.database.Cursor} cursor 查询结果 * @returns 结果转为数组 */ static resultToArray(cursor) { let result = []; if (cursor.getCount() == 0) { return result; } let columns = cursor.getColumnCount(); const typeMap = new Map(); cursor.moveToFirst(); for (let index = 0; index < columns; index++) { typeMap.set(index, [cursor.getColumnName(index), cursor.getType(index)]); } cursor.moveToPosition(-1); while (cursor.moveToNext()) { const student = {}; for (let index = 0; index < columns; index++) { let value; /** @type {[string,string]}*/ let info = typeMap.get(index); let type = info[1]; switch (type) { case 0: value = null; case 1: value = cursor.getInt(index); break; case 2: value = cursor.getDouble(index); break; case 3: value = cursor.getString(index); break; case 4: value = cursor.getBlob(index); break; } student[info[0]] = value; } result.push(student); } cursor.close(); return result; } } class Model { /**@type {android.database.sqlite.SQLiteDatabase} */ #db; #tableName; constructor(db, tableName) { this.#db = db; this.#tableName = tableName; } get tableName() { return this.#tableName; } insertItem(data, update = false) { const values = SQLiteHelper.dataToContent(data); // 插入一条数据 try { return this.#db.insertOrThrow(this.#tableName, null, values); } catch (error) { /**@type {string} */ let message = error.message.split("\n")[0]; let unique_column = message.match(/UNIQUE constraint failed: .+?\.(.+?) /); if (update && unique_column) { let column = unique_column[1]; let item = this.findOne({ [column]: data[column] }); this.update(data, item); const pk = this.#db.rawQuery(`SELECT col.name FROM pragma_table_info (?) as col WHERE col.pk = 1;`, [this.#tableName]); const pkName = SQLiteHelper.resultToArray(pk)[0].name; return item[pkName]; } else { console.error(message); return -1; } } } /** * 添加数据 * @param {object[]} data 数据 */ insert(data, update = false) { let result = []; for (const item of data) { result.push(this.insertItem(item, update)); } return result; } delete(where) { let rawWhere = SQLiteHelper.toWhereClause(where); return this.#db.delete(this.#tableName, rawWhere.clause, rawWhere.args); } update(value, where) { const values = SQLiteHelper.dataToContent(value); const whereInfo = SQLiteHelper.toWhereClause(where); return this.#db.update(this.#tableName, values, whereInfo.clause, whereInfo.args); } query(where) { let whereClause = where ? SQLiteHelper.toWhereClause(where) : { args: [] }; let rawWhere = where ? `WHERE ${whereClause.clause}` : ""; const cursor = this.#db.rawQuery(`SELECT * FROM ${this.#tableName} ${rawWhere}`, whereClause.args); return SQLiteHelper.resultToArray(cursor); } findOne(where) { let rawWhere = SQLiteHelper.toWhereClause(where); const cursor = this.#db.rawQuery(`SELECT * FROM ${this.#tableName} WHERE ${rawWhere.clause} limit 1`, rawWhere.args); return cursor.getCount() ? SQLiteHelper.resultToArray(cursor)[0] : null; } findByPk(id) { const pk = this.#db.rawQuery(`SELECT col.name FROM pragma_table_info (?) as col WHERE col.pk = 1;`, [this.#tableName]); const pkName = SQLiteHelper.resultToArray(pk)[0].name; id = [String(id)]; /* const pk = this.#db.rawQuery(`pragma table_info ('${this.#tableName}')`, null); const arr = SQLiteHelper.resultToArray(pk); const pkInfo = arr.find(item => item.pk === 1); id = [String(id)]; */ const cursor = this.#db.rawQuery(`SELECT * FROM ${this.#tableName} WHERE ${pkName} = ?`, id); return cursor.getCount() ? SQLiteHelper.resultToArray(cursor)[0] : null; } count(where) { let whereClause = where ? SQLiteHelper.toWhereClause(where) : { args: [] }; let rawWhere = where ? `WHERE ${whereClause.clause}` : ""; const cursor = this.#db.rawQuery(`SELECT * FROM ${this.#tableName} ${rawWhere}`, rawWhere.args); return cursor.getCount(); } } export class SQLite { #db; emitter; /** * 构造一个数据string * @param {string} name * @param {object} config * @return sqlite数据库 */ constructor(name, config = {}) { // 数据库路径 const dbPath = path.join(process.cwd(), `${name}.db`); if (config.force) { fs.rmSync(dbPath, { force: true }); } const sqlHelper = new SQLHelper($autojs.androidContext, dbPath, config.version ?? 1); const db = config.readonly ? sqlHelper.getReadableDatabase() : sqlHelper.getWritableDatabase(); // 设置数据库对象的方法均在io线程执行,避免阻塞node.js当前线程 // $java.setThreadMode(db, "io"); this.emitter = new EventEmitter(); this.#db = db; } get version() { return this.#db.getVersion(); } get pageSize() { return this.#db.getPageSize(); } get maximumSize() { return this.#db.getMaximumSize(); } get path() { return this.#db.getPath(); } get inTransaction() { return this.#db.inTransaction(); } getTable(tableName) { let arr = this.rawQuery("SELECT * FROM sqlite_master WHERE name=?", [tableName]); return arr.length ? new Model(this.#db, tableName) : null; } createTable(tableName, attributes) { //生成sql语句 const sql = SQLiteHelper.toRawCreate(tableName, attributes); this.#db.execSQL(sql); return new Model(this.#db, tableName); } rawCreateTable(tableName, sql) { this.#db.execSQL(sql); return new Model(this.#db, tableName); } /** * 使用原始语句查询 * @param {string} sql 原始语句 * @param {any[]} binding 绑定的值 */ rawQuery(sql, binding = []) { let strArr = binding.map(item => String(item)); const cursor = this.#db.rawQuery(sql, strArr); return SQLiteHelper.resultToArray(cursor); } rawSql(sql, binding) { try { if (binding !== undefined) { let strArr = binding.map(item => String(item)); this.#db.execSQL(sql, strArr); } else { this.#db.execSQL(sql); } } catch (error) { console.error(error); } } /** * * @param {(sqlite:this)=>void} callback * @returns */ begin(callback) { let emitter = this.emitter; this.#db.beginTransactionWithListener({ onBegin() { emitter.emit("begin"); }, onCommit() { emitter.emit("commit"); }, onRollback() { emitter.emit("rollback"); }, }); try { callback?.(this); } catch (error) { let message = error.message.split("\n")[0]; console.error(message); this.endTransaction(); } return emitter; } end() { try { this.#db.endTransaction(); } catch (error) { let message = error.message.split("\n")[0]; console.error(message); } } succeed() { try { this.#db.setTransactionSuccessful(); } catch (error) { let message = error.message.split("\n")[0]; console.error(message); } } close() { this.#db.close(); } merge() { this.succeed(); this.end(); } }