This commit is contained in:
2026-09-24 16:25:22 +08:00
commit 7428184f01
1198 changed files with 314515 additions and 0 deletions
+15
View File
@@ -0,0 +1,15 @@
## 1.0.3 (2025-09-03)
* 适配鸿蒙
## 1.0.2 (2025-05-18)
* 修复安卓端执行插入数据时,可能会被执行两次。修复安卓/ios端不能使用应用的uni.env.USER_DATA_PATH目录及自定二级目录,在安卓端执行COUNT(*)时类型转换错误。
## 1.0.1 (2025-05-05)
* 修复安卓端执行插入数据时,可能会被执行两次。
## 1.0.0 (2025-03-15)
### ✨ 新功能
- 支持基本的SQLite3数据库操作
- 数据库的创建、打开和关闭
- 表的创建和删除
- 数据的增删改查操作
- 支持数据库加密功能
- 支持事务处理
- 支持批量SQL执行
+83
View File
@@ -0,0 +1,83 @@
{
"id": "x-sqlite-s",
"displayName": "x-sqlite-s",
"version": "1.0.0",
"description": "x-sqlite-s",
"keywords": [
"x-sqlite-s"
],
"repository": "",
"engines": {
"HBuilderX": "^3.6.8"
},
"dcloudext": {
"type": "uts",
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "",
"data": "",
"permissions": ""
},
"npmurl": ""
},
"uni_modules": {
"dependencies": [],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "u",
"aliyun": "u",
"alipay": "u"
},
"client": {
"Vue": {
"vue2": "u",
"vue3": "u"
},
"App": {
"app-android": "u",
"app-ios": "u",
"app-harmony": "u"
},
"H5-mobile": {
"Safari": "u",
"Android Browser": "u",
"微信浏览器(Android)": "u",
"QQ浏览器(Android)": "u"
},
"H5-pc": {
"Chrome": "u",
"IE": "u",
"Edge": "u",
"Firefox": "u",
"Safari": "u"
},
"小程序": {
"微信": "u",
"阿里": "u",
"百度": "u",
"字节跳动": "u",
"QQ": "u",
"钉钉": "u",
"快手": "u",
"飞书": "u",
"京东": "u"
},
"快应用": {
"华为": "u",
"联盟": "u"
}
}
}
}
}
+218
View File
@@ -0,0 +1,218 @@
# x-sqlite-s
## 简介
x-sqlite-s 是一个用于 uni-appx 的 SQLite 数据库操作插件,支持基本的数据库。
### 兼容性
| Harmony | IOS | Android | WEB | 小程序 |
| --- | --- | --- | --- | --- |
| 支持 | 支持 | 支持 | 支持 | x |
## 安装
在插件市场搜索并导入 x-sqlite-s 插件。
## 特别注意
配置中的密码等加密功能,未实现,只是一个预设功能.
## web注意事项
请到插件目录内复制static到你的根目录static即可。否则web无法使用。
## 类型定义
### SQLiteConfig 配置项
**鸿蒙不支持自定义其它目录**为了全平台兼容,不建议设置defaultDirectory
在安卓和ios中defaultDirectory它可指定为缓存目录,下载目录,用户目录等等沙盒目录。
但鸿蒙它是固定在`应用目录/rdb/自定义目录`,下,因为实际上是被固定在了`应用目录/rdb/`下.
因此差异非常大。故,鸿蒙端defaultDirectory我将不会生效。仅在安卓和ios下生效。就算你写了我也不使用。
```typescript
type SQLiteConfig = {
password?: string; // 数据库密码
encryption?: boolean; // 是否启用加密
defaultDirectory?: string; // 数据库文件存储目录,鸿蒙不支持,它的目录是固定的加密区目录,无法像安卓,ios那样保存到其它目录:比如用户目录,缓存目录等。
locateFile?: (filename: string) => string; // 自定义文件位置,web专用属性,默认内置
}
```
### SQLiteResult 返回结果
```typescript
type SQLiteResult = {
rows?: any[][]; // 查询结果行数据
columns?: string[]; // 列名
maps?: any[]; // 查询结果对象数组,可通过列名访问数据
changes?: number; // 影响的行数
lastInsertRowid?: number; // 最后插入的行ID
error?: string; // 错误信息
}
```
### SQLiteExecuteBatchParams 批量执行参数
```typescript
type SQLiteExecuteBatchParams = {
sql: string; // SQL语句
params?: any[]; // 参数列表
}
```
## API 说明
### 创建数据库
```javascript
createDb(filename?: string): boolean
// web端是Promise函数,执行时可以await createDb
// app是同步操作,不是Promise
```
创建或打开一个数据库。如果未指定文件名,默认为 "sqlite.db"。
### 执行SQL查询
```javascript
run(sql: string, params: any[] = []): SQLiteResult
```
执行一条SQL语句,支持参数绑定。
### 查询数据
```javascript
query(sql: string, params: any[] = []): SQLiteResult
```
执行查询语句并返回结果集。
### 插入数据
```javascript
insert(table: string, data: object): SQLiteResult
```
向指定表插入数据。
### 更新数据
```javascript
update(table: string, data: object, where: string, params: any[] = []): SQLiteResult
```
更新表中的数据。
### 删除数据
```javascript
delete(table: string, where: string, params: any[] = []): SQLiteResult
```
删除表中的数据。
### 事务处理
```javascript
beginTransaction(): SQLiteResult // 开始事务
commit(): SQLiteResult // 提交事务
rollback(): SQLiteResult // 回滚事务
```
### 批量执行
```javascript
executeBatch(statements: SQLiteExecuteBatchParams[]): SQLiteResult[]
```
批量执行SQL语句,支持事务。
### 数据库文件管理
**createDb,loadFromBuffer,loadLocal三个函数**
**web端是Promise函数**
**app是同步操作,不是Promise**
```javascript
createDb(filename?: string): boolean // 创建一个本地数据库,重命的重复创建不会覆盖,只是读取已有的数据库.
saveLocal(filename?: string): SQLiteResult // 保存数据库到本地
loadLocal(filename: string): SQLiteResult // 从本地加载数据库
getDatabasePath(): string | null // app端获取数据库备份临时文件路径
getDatabasePath(): Uint8Array | null // web端返回的是二进制数据
loadFromBuffer(buffer: Uint8Array):void // web端专用函数,用来从进制制数据加载远程数据库
setDefaultDirectory(directory: string): void // 设置数据库文件存储目录
setPassword(password: string|null): void // 设置数据库密码,null或空值表示删除密码
```
### 表操作
```javascript
tableExists(tableName: string): boolean // 检查表是否存在
createTable(tableName: string, columns: UTSJSONObject): SQLiteResult // 创建表
dropTable(tableName: string): SQLiteResult // 删除表
```
## 使用示例
### 基本操作
```javascript
import {xSqlite} from "@/uni_modules/x-sqlite-s"
// 创建数据库实例并配置
const db = new xSqlite({
encryption: true,
password: "mypassword",
defaultDirectory: "/custom/db/path"
});
// 创建数据库
db.createDb("mydb");
// 创建表
db.createTable("users", {
"id": "INTEGER PRIMARY KEY AUTOINCREMENT",
"name": "TEXT",
"age": "INTEGER"
});
// 插入数据
db.insert("users", {
name: "张三",
age: 25
});
// 查询数据
const result = db.query("SELECT * FROM users WHERE age > ?", [20]);
console.log(result.rows); // 通过数组下标访问:[[1, "张三", 25], [2, "李四", 30]]
console.log(result.maps); // 通过列名访问:[{"id": 1, "name": "张三", "age": 25}, {"id": 2, "name": "李四", "age": 30}]
```
### 事务使用
```javascript
// 开始事务
db.beginTransaction();
try {
db.insert("users", { name: "李四", age: 30 });
db.insert("users", { name: "王五", age: 35 });
db.commit();
} catch (e) {
db.rollback();
}
```
### 批量操作
```javascript
const statements = [
{
sql: "INSERT INTO users (name, age) VALUES (?, ?)",
params: ["赵六", 40]
},
{
sql: "UPDATE users SET age = ? WHERE name = ?",
params: [45, "张三"]
}
];
db.executeBatch(statements);
```
### 数据库配置管理
```javascript
// 更改数据库存储目录
db.setDefaultDirectory("/new/db/path");
// 更新数据库密码
db.setPassword("newpassword");
// 移除数据库密码
db.setPassword(null);
```
## 注意事项
1. 在进行数据库操作前,确保已经成功创建/打开数据库
2. 使用参数绑定来防止SQL注入
3. 大量数据操作时建议使用事务
4. 及时关闭不再使用的数据库连接
5. 加密数据库需要在创建时指定密码
@@ -0,0 +1,4 @@
{
"minSdkVersion": "21",
"dependencies":[]
}
@@ -0,0 +1,362 @@
import xSqliteHelp from 'uts.sdk.modules.utsXsqliteS.xSqliteHelp';
import SQLiteResultByKt from 'uts.sdk.modules.utsXsqliteS.SQLiteResultByKt';
import SQLiteConfigByKt from 'uts.sdk.modules.utsXsqliteS.SQLiteConfigByKt';
import SQLiteExecuteBatchParamsByKt from 'uts.sdk.modules.utsXsqliteS.SQLiteExecuteBatchParamsByKt';
import {SQLiteConfig,SQLiteResult,SQLiteExecuteBatchParams} from '../interface.uts';
import Kotlin from 'kotlin.jvm.internal.Intrinsics.Kotlin';
/**
* SQLite数据库操作类
* 提供SQLite数据库的创建、查询、更新等基本操作,支持加密和事务处理
*/
export class xSqlite {
/** 数据库配置信息 */
config : SQLiteConfig;
private db : xSqliteHelp|null = null;
constructor(config : SQLiteConfig = {}) {
let realCofing:SQLiteConfig = config;
let directory:null|string = realCofing?.defaultDirectory??null
if(typeof directory == 'string' && directory !=null){
directory = UTSAndroid.convert2AbsFullPath(directory)
}
realCofing.defaultDirectory = directory
this.config = realCofing;
this.db = new xSqliteHelp(
UTSAndroid.getAppContext()!,
SQLiteConfigByKt(
password = this.config.password,
encryption = (this.config?.encryption??false),
defaultDirectory = (this.config?.defaultDirectory??null),
)
)
}
private converFormat(result?:SQLiteResultByKt):SQLiteResult{
if(result==null){
return {
rows: [] ,
columns: [] ,
changes: null,
lastInsertRowid: null,
error: null,
};
}
let rows = [] as any[][];
let maps = [] as Map<string,any>[];
let columns = [] as string[];
if(result?.rows!=null){
let cols = result!.columns as ArrayList<string>;
columns = Array.fromNative(cols)
}
if(result?.rows!=null){
let crows = result!.rows as ArrayList<ArrayList<any>>;
for (col in crows) {
rows.push(Array.fromNative(col))
}
}
if(result?.maps!=null){
let cmaps = result!.maps as ArrayList<LinkedHashMap<string,any>>;
for (col in cmaps) {
let item = col as LinkedHashMap<string,any>;
let newmap = new Map<string,any>()
for(key in columns){
newmap.set(key,item.get(key)!)
}
maps.push(newmap)
}
}
return {
rows: rows,
columns: columns,
maps: maps,
changes: result?.changes,
lastInsertRowid: result?.lastInsertRowid,
error: result?.error,
};
}
/**
* 创建数据库
* @param fileName 数据库文件名,如果为null则使用默认名称'sqlite'
* @returns 创建成功返回true,失败返回false
*/
createDb(fileName:string|null = null) : boolean{
if (this.db==null){
return false;
}
const result = this.db!.createDb(fileName)
return result != null;
}
/**
* 执行SQL语句
* @param sql SQL语句
* @param params SQL参数数组
* @returns SQLiteResult 执行结果,包含changes(影响行数)和lastInsertRowid(最后插入行ID)
*/
run(sql: string, params: any[] = []): SQLiteResult {
try {
if (this.db==null){
return {error:'Database not initialized'};
}
const result = this.db!.run(sql, params);
return this.converFormat(result)
} catch (error) {
return { error: error.message };
}
}
/**
* 查询数据
* @param sql 查询SQL语句
* @param params 查询参数数组
* @returns SQLiteResult 查询结果,包含rows(数据行)和columns(列名)
*/
query(sql: string, params: any[] = [] as any[]): SQLiteResult {
if (this.db==null){
return {error:'Database not initialized'};
}
const result = this.db!.query(sql, params.toKotlinList());
return this.converFormat(result)
}
/**
* 插入数据
* @param table 表名
* @param data 要插入的数据对象
* @returns SQLiteResult 插入结果
*/
insert(table: string, data: UTSJSONObject): SQLiteResult {
if (this.db==null){
return {error:'Database not initialized'};
}
const result = this.db!.insert(table, new org.json.JSONObject(data.toJSONString()));
return this.converFormat(result)
}
/**
* 更新数据
* @param table 表名
* @param data 要更新的数据对象
* @param where WHERE条件语句
* @param params WHERE条件参数数组
* @returns SQLiteResult 更新结果
*/
update(table: string, data: UTSJSONObject, where: string, params: any[] = []): SQLiteResult {
if (this.db==null){
return {error:'Database not initialized'};
}
const result = this.db!.update(table,
new org.json.JSONObject(data.toJSONString()),
where,
params.toKotlinList()
);
return this.converFormat(result)
}
/**
* 删除数据
* @param table 表名
* @param where WHERE条件语句
* @param params WHERE条件参数数组
* @returns SQLiteResult 删除结果
*/
delete(table: string, where: string, params: any[] = []): SQLiteResult {
if (this.db==null){
return {error:'Database not initialized'};
}
const result = this.db!.delete(table, where ,params.toKotlinList());
return this.converFormat(result)
}
/**
* 检查表是否存在
* @param tableName 表名
* @returns 表存在返回true,不存在返回false
*/
tableExists(tableName: string): boolean {
if (this.db==null){
return false
}
return this.db!.tableExists(tableName);
}
/**
* 创建数据表
* @param tableName 表名
* @param columns 列定义对象,key为列名,value为列类型定义
* @returns SQLiteResult 创建结果
*/
createTable(tableName: string, columns: UTSJSONObject): SQLiteResult {
try {
if (this.db==null){
return {error:'Database not initialized'};
}
const dataMap = new Map<string,string>();
for(const key in columns){
const item = columns.getString(key)!;
dataMap.set(key,item)
}
const result = this.db!.createTable(tableName, dataMap);
return this.converFormat(result)
} catch (error) {
return { error: error.message };
}
}
/**
* 删除数据表
* @param tableName 要删除的表名
* @returns SQLiteResult 删除结果
*/
dropTable(tableName: string): SQLiteResult {
if (this.db==null){
return {error:'Database not initialized'};
}
const result = this.db!.dropTable(tableName);
return this.converFormat(result)
}
/**
* 关闭数据库连接
* 释放数据库资源,关闭后需要重新创建才能使用
*/
close(): void {
if (this.db!=null) {
this.db!.close();
this.db = null;
}
}
/**
* 开始事务
* 开始一个新的事务,在提交或回滚之前,所有操作都在事务内
* @returns SQLiteResult 事务开始结果
*/
beginTransaction(): SQLiteResult{
if (this.db==null){
return {error:'Database not initialized'};
}
const result = this.db!.beginTransaction();
return this.converFormat(result)
}
/**
* 提交事务
* 提交当前事务的所有操作
* @returns SQLiteResult 事务提交结果
*/
commit(): SQLiteResult {
if (this.db==null){
return {error:'Database not initialized'};
}
const result = this.db!.commit();
return this.converFormat(result)
}
/**
* 回滚事务
* 撤销当前事务中的所有操作
* @returns SQLiteResult 事务回滚结果
*/
rollback(): SQLiteResult {
if (this.db==null){
return {error:'Database not initialized'};
}
const result = this.db!.rollback();
return this.converFormat(result)
}
/**
* 批量执行SQL语句
* @param statements SQL语句数组,每个元素包含sql和params
* @returns SQLiteResult[] 每个语句的执行结果数组
*/
executeBatch(statements: SQLiteExecuteBatchParams[]): SQLiteResult[] {
if (this.db==null){
console.error('Database not initialized')
return [];
}
const list = new ArrayList<SQLiteExecuteBatchParamsByKt>()
for(const item in statements){
list.add(SQLiteExecuteBatchParamsByKt(
sql = item.sql,
params = (item?.params??[]).toKotlinList()
))
}
const result = this.db!.executeBatch(list);
let resultList = [] as SQLiteResult[]
for(const item in result){
resultList.push(this.converFormat(item))
}
return resultList
}
/**
* 保存数据库到本地文件
* @param filename 保存的文件名,可选,默认为'sqlite'
* @returns SQLiteResult 保存结果
*/
saveLocal(filename?: string): SQLiteResult {
if (this.db==null){
return {error:'Database not initialized'};
}
const result = this.db!.saveLocal(filename);
return this.converFormat(result)
}
/**
* 从本地文件加载数据库
* @param filename 要加载的数据库文件名
* @returns SQLiteResult 加载结果
*/
loadLocal(filename:string): SQLiteResult{
const result = this.db!.loadLocal(filename);
return this.converFormat(result)
}
/**
* 获取数据库文件路径
* 获取当前数据库文件的完整路径,获取前会备份到缓存目录
* @returns string|null 数据库文件路径,如果数据库未初始化则返回null
*/
getDatabasePath():string|null{
if (this.db==null){
console.error('Database not initialized')
return null
}
return this.db!.getDatabasePath();
}
/**
* 当前数据库的目录
*/
setDefaultDirectory(directory: string){
if (this.db==null){
console.error('Database not initialized')
return
}
let rdirectory = directory;
if(rdirectory!=''){
rdirectory = UTSAndroid.convert2AbsFullPath(directory)
}
this.db!.setDefaultDirectory(rdirectory);
}
/**
* 设置数据库密码
* 空值或者null即删除密码
*/
setPassword(password: string|null = null){
if (this.db==null){
console.error('Database not initialized')
return
}
this.db!.setPassword(password);
}
}
@@ -0,0 +1,364 @@
package uts.sdk.modules.utsXsqliteS
import android.content.Context
import android.database.sqlite.SQLiteDatabase
import android.database.sqlite.SQLiteOpenHelper
import org.json.JSONObject
import java.io.File
import io.dcloud.uts.console
data class SQLiteConfigByKt(
var password: String? = null,
var encryption: Boolean = false,
var locateFile: ((String) -> String)? = null,
var defaultDirectory: String? = null
)
data class SQLiteResultByKt(
val rows: List<List<Any>>? = null,
val columns: List<String>? = null,
val changes: Int? = null,
val lastInsertRowid: Long? = null,
val error: String? = null,
val maps: List<Map<String, Any>>? = null
)
data class SQLiteExecuteBatchParamsByKt(
val sql: String,
val params: List<Any>? = null
)
class xSqliteHelp(private val context: Context, private val config: SQLiteConfigByKt = SQLiteConfigByKt()) {
private var db: SQLiteDatabase? = null
private var isInTransaction: Boolean = false
private var fileURLPath: String? = null
fun createDb(filename: String? = null): SQLiteDatabase? {
val fname = filename ?: "sqlite"
val dbFile = if (config.defaultDirectory != null) {
File(config.defaultDirectory, "$fname.db")
} else {
context.getDatabasePath("$fname.db")
}
try {
if (!dbFile.parentFile?.exists()!!) {
dbFile.parentFile?.mkdirs()
}
db = SQLiteDatabase.openOrCreateDatabase(dbFile, null)
if (config.encryption && config.password != null) {
run("PRAGMA key = ?", listOf(config.password!!))
}
fileURLPath = dbFile.absolutePath
return db
} catch (e: Exception) {
e.printStackTrace()
return null
}
}
// 执行SQL查询
fun run(sql: String, params: List<Any> = emptyList()): SQLiteResultByKt {
return try {
if (db == null) throw Exception("Database not initialized")
val statement = db!!.compileStatement(sql)
params.forEachIndexed { index, param ->
when (param) {
is String -> statement.bindString(index + 1, param)
is Int -> statement.bindLong(index + 1, param.toLong())
is Long -> statement.bindLong(index + 1, param)
is Double -> statement.bindDouble(index + 1, param)
is ByteArray -> statement.bindBlob(index + 1, param)
null -> statement.bindNull(index + 1)
}
}
// 根据SQL语句类型选择合适的执行方法
val sqlUpperCase = sql.trim().uppercase()
val result = if (sqlUpperCase.startsWith("INSERT")) {
// 对于INSERT语句,使用executeInsert获取lastInsertRowid
val lastId = statement.executeInsert()
SQLiteResultByKt(changes = 1, lastInsertRowid = lastId)
} else {
// 对于UPDATE/DELETE/其他语句,使用executeUpdateDelete获取受影响的行数
val changes = statement.executeUpdateDelete()
SQLiteResultByKt(changes = changes, lastInsertRowid = null)
}
result
} catch (e: Exception) {
SQLiteResultByKt(error = e.message)
}
}
// 查询数据
fun query(sql: String, params: List<Any> = emptyList()): SQLiteResultByKt {
return try {
if (db == null) throw Exception("Database not initialized")
val args = params.map { it?.toString() ?: "" }.toTypedArray()
val cursor = db!!.rawQuery(sql, args)
val columns = ArrayList(cursor.columnNames.toList())
val rows = mutableListOf<List<Any>>()
val maps = mutableListOf<Map<String, Any>>()
while (cursor.moveToNext()) {
val row = mutableListOf<Any>()
val map = mutableMapOf<String, Any>()
for (i in 0 until cursor.columnCount) {
val columnName = cursor.getColumnName(i)
val value = when (cursor.getType(i)) {
android.database.Cursor.FIELD_TYPE_STRING -> cursor.getString(i)
android.database.Cursor.FIELD_TYPE_INTEGER -> cursor.getLong(i)
android.database.Cursor.FIELD_TYPE_FLOAT -> cursor.getDouble(i)
android.database.Cursor.FIELD_TYPE_BLOB -> cursor.getBlob(i)
else -> ""
}
row.add(value)
map[columnName] = value
}
rows.add(row)
maps.add(map)
}
cursor.close()
SQLiteResultByKt(rows = rows, columns = columns, maps = maps)
} catch (e: Exception) {
SQLiteResultByKt(error = e.message)
}
}
// 插入数据
fun insert(table: String, data: JSONObject): SQLiteResultByKt {
val keys = mutableListOf<String>()
val values = mutableListOf<Any>()
data.keys().forEach { key ->
keys.add(key)
values.add(data.get(key))
}
val sql = "INSERT INTO $table (${keys.joinToString(",")}) VALUES (${keys.map { "?" }.joinToString(",")})"
return run(sql, values)
}
// 更新数据
fun update(table: String, data: JSONObject, where: String, params: List<Any> = emptyList()): SQLiteResultByKt {
val sets = mutableListOf<String>()
val values = mutableListOf<Any>()
data.keys().forEach { key ->
sets.add("$key = ?")
values.add(data.get(key))
}
values.addAll(params)
val sql = "UPDATE $table SET ${sets.joinToString(",")} WHERE $where"
return run(sql, values)
}
// 删除数据
fun delete(table: String, where: String, params: List<Any> = emptyList()): SQLiteResultByKt {
val sql = "DELETE FROM $table WHERE $where"
return run(sql, params)
}
// 保存数据库到本地
fun saveLocal(filename: String? = null): SQLiteResultByKt {
return try {
if (db == null) throw Exception("Database not initialized")
val fname = filename ?: "sqlite"
// 获取当前数据库文件的实际路径
val dbPath = db!!.path
val dbFile = if (dbPath.isNotEmpty()) File(dbPath) else context.getDatabasePath("sqlite.db")
// 检查源文件是否存在
if (!dbFile.exists()) {
return SQLiteResultByKt(error = "未能找到数据库文件")
}
// 使用配置的默认目录或系统文件目录
val targetDir = if (config.defaultDirectory != null) File(config.defaultDirectory) else context.filesDir
val targetFile = File(targetDir, "$fname.db")
// 确保目标目录存在
targetFile.parentFile?.mkdirs()
if(dbFile.absolutePath != targetFile.absolutePath){
dbFile.copyTo(targetFile, overwrite = true)
}
SQLiteResultByKt()
} catch (e: Exception) {
SQLiteResultByKt(error = e.message)
}
}
// 从本地加载数据库
fun loadLocal(filename: String): SQLiteResultByKt {
return try {
// 使用配置的默认目录或系统文件目录
val sourceDir = if (config.defaultDirectory != null) File(config.defaultDirectory!!) else context.filesDir
val sourceFile = File(sourceDir, "$filename.db")
if (!sourceFile.exists()) {
return SQLiteResultByKt(error = "没有数据库")
}
val dbFile = context.getDatabasePath("sqlite.db")
sourceFile.copyTo(dbFile, overwrite = true)
createDb(filename)
SQLiteResultByKt()
} catch (e: Exception) {
SQLiteResultByKt(error = "数据库损坏")
}
}
// 设置默认目录
fun setDefaultDirectory(directory: String) {
config.defaultDirectory = directory
}
// 设置密码
fun setPassword(password: String?) {
config.password = password
config.encryption = password != null && password.isNotEmpty()
if (db != null) {
if (config.encryption && config.password != null) {
run("PRAGMA rekey = ?", listOf(config.password!!))
} else {
run("PRAGMA rekey = ''", emptyList())
}
}
}
// 检查表是否存在
fun tableExists(tableName: String): Boolean {
val result = query("SELECT name FROM sqlite_master WHERE type='table' AND name=?", listOf(tableName))
return result.rows?.isNotEmpty() == true
}
// 创建数据表
fun createTable(tableName: String, columns: Map<String, String>): SQLiteResultByKt {
val columnDefinitions = columns.entries.joinToString(",") { (name, type) -> "$name $type" }
val sql = "CREATE TABLE IF NOT EXISTS $tableName ($columnDefinitions)"
return run(sql)
}
// 删除数据表
fun dropTable(tableName: String): SQLiteResultByKt {
val sql = "DROP TABLE IF EXISTS $tableName"
return run(sql)
}
// 关闭数据库
fun close() {
db?.close()
db = null
}
// 开始事务
fun beginTransaction(): SQLiteResultByKt {
return try {
if (isInTransaction) {
return SQLiteResultByKt(error = "Transaction already in progress")
}
db?.beginTransaction()
isInTransaction = true
SQLiteResultByKt()
} catch (e: Exception) {
SQLiteResultByKt(error = e.message)
}
}
// 提交事务
fun commit(): SQLiteResultByKt {
return try {
if (!isInTransaction) {
return SQLiteResultByKt(error = "No transaction in progress")
}
db?.setTransactionSuccessful()
db?.endTransaction()
isInTransaction = false
SQLiteResultByKt()
} catch (e: Exception) {
SQLiteResultByKt(error = e.message)
}
}
// 回滚事务
fun rollback(): SQLiteResultByKt {
return try {
if (!isInTransaction) {
return SQLiteResultByKt(error = "No transaction in progress")
}
db?.endTransaction()
isInTransaction = false
SQLiteResultByKt()
} catch (e: Exception) {
SQLiteResultByKt(error = e.message)
}
}
// 批量执行SQL
fun executeBatch(statements: List<SQLiteExecuteBatchParamsByKt>): List<SQLiteResultByKt> {
val results = mutableListOf<SQLiteResultByKt>()
var hasError = false
val wasInTransaction = isInTransaction
if (!wasInTransaction) {
beginTransaction()
}
try {
for (stmt in statements) {
val result = run(stmt.sql, stmt.params ?: emptyList())
results.add(result)
if (result.error != null) {
hasError = true
break
}
}
if (hasError && !wasInTransaction) {
rollback()
} else if (!wasInTransaction) {
commit()
}
} catch (e: Exception) {
if (!wasInTransaction) {
rollback()
}
results.add(SQLiteResultByKt(error = e.message))
}
return results
}
/**
* 获取数据库文件路径获取前会备份到缓存目录
*/
fun getDatabasePath(): String? {
if (db == null) return null
try {
// 获取当前数据库文件
// val dbFile = db!!.path
val dbFile = File(db!!.path)
if (!dbFile.exists() || !dbFile.canRead()) {
return null
}
// 获取缓存目录
val cacheDir = context.cacheDir
val tempFile = File(cacheDir, "tmui4x_xSqlite_DbackTemp.db")
// 如果临时文件存在则删除
if (tempFile.exists()) {
tempFile.delete()
}
// 复制数据库文件到缓存目录
dbFile.copyTo(tempFile, overwrite = true)
return tempFile.absolutePath
} catch (e: Exception) {
return null
}
}
}
@@ -0,0 +1,404 @@
import { ValueType, ValuesBucket, relationalStore } from '@kit.ArkData';
import { featureAbility } from '@kit.AbilityKit';
import { BusinessError } from '@ohos.base';
interface SQLiteConfig {
password ?: string;
encryption ?: boolean;
locateFile ?: (filename : string) => string;
defaultDirectory ?: string;
}
interface SQLiteResult {
rows ?: any[][]|null;
columns ?: string[]|null;
changes ?: number|null;
lastInsertRowid ?: number|null;
error ?: string|null;
maps ?: Map<string, any>[]|null
}
interface SQLiteExecuteBatchParams {
sql : string,
params ?: any[]|null
}
interface SQLiteDbFileInfo {
path : string | null,
size : number | null
}
export class xSqlite {
/** 数据库配置信息 */
config : relationalStore.StoreConfig = {
name: "sqlite.db",
securityLevel: relationalStore.SecurityLevel.S3,
customDir: 'tmui4x'
};
private isInTransaction:boolean = false;
private db : relationalStore.RdbStore | null = null;
constructor(config ?: SQLiteConfig) {
}
private converFormat(result?:SQLiteResult):SQLiteResult{
if(result==null){
return {
rows: [] ,
columns: [] ,
changes: null,
lastInsertRowid: null,
error: null,
};
}
return {
rows: (result?.rows==null?[]:result?.rows),
columns: (result?.columns==null?[]:result?.columns),
changes: (result?.changes==null?null:result?.changes),
maps: (result?.maps==null?null:result?.maps),
lastInsertRowid: (result?.lastInsertRowid==null?null:result?.lastInsertRowid),
error: (result?.error==null?null:result?.error),
};
}
/**
* 创建数据库
* @param fileName 数据库文件名,如果为null则使用默认名称'sqlite'
* @returns 创建成功返回true,失败返回false
*/
async createDb(fileName : string | null = null) : Promise<boolean> {
if (this.db != null) {
return Promise.resolve(true);
}
this.config.name = fileName ? (fileName + '.db') : this.config.name
const context = UTSHarmony.getUIAbilityContext()
return new Promise<boolean>((res, rej) => {
relationalStore.getRdbStore(context, this.config).then(async (rdbStore : relationalStore.RdbStore) => {
this.db = rdbStore;
console.info('Get RdbStore successfully.');
res(true)
}).catch((err : BusinessError) => {
console.error(`Get RdbStore failed, code is ${err.code},message is ${err.message}`);
rej(false)
});
})
}
/**
* 关闭数据库连接
* 释放数据库资源,关闭后需要重新创建才能使用
*/
async close(): Promise<boolean> {
if (this.db!=null) {
this.db = null;
try{
await this.db!.close();
return Promise.resolve(true)
}catch(e){
return Promise.resolve(false)
}
}
return Promise.resolve(true)
}
/**
* 鸿蒙不支持,此函数仅为兼容用,执行没有任反应。
* @param filename 保存的文件名,可选,默认为'sqlite'
* @returns SQLiteResult 保存结果
*/
saveLocal(filename?: string): SQLiteResult {
const context = UTSHarmony.getCurrentWindow()!.getUIContext();
console.warn('harmoney不支持本函数')
return this.converFormat()
}
/**
* 从本地文件加载数据库,它同createDb是一样的效果,有就加载,没有就创建。
* @param filename 要加载的数据库文件名
* @returns SQLiteResult 加载结果
*/
async loadLocal(filename:string): Promise<SQLiteResult>{
try{
if(this.db){
await this.db.close();
}
this.config.name = filename+'.db'
await this.createDb(this.config.name)
return Promise.resolve(this.converFormat())
}catch(e){
return Promise.resolve(this.converFormat({error:"错误"} as SQLiteResult))
}
}
/**
* 执行SQL语句
* @param sql SQL语句
* @param params SQL参数数组
* @returns SQLiteResult 执行结果,包含changes(影响行数)和lastInsertRowid(最后插入行ID)
*/
run(sql: string, params: any[] = []): SQLiteResult {
try {
if (!this.db) return {error:'Database not initialized'}
const result = this.db.executeSync(sql,params as relationalStore.ValueType[])
console.log('TMUI',`Sqlite:${result}`)
return {};
} catch (error) {
let er = error as BusinessError
console.error("TMUI",`Sqlite:${er.message}`)
return { error: er.message };
}
}
/**
* 开始事务
* 开始一个新的事务,在提交或回滚之前,所有操作都在事务内
* @returns SQLiteResult 事务开始结果
*/
beginTransaction(): SQLiteResult{
if (!this.db) return {error:'Database not initialized'}
if (this.isInTransaction) {
return { error: 'Transaction already in progress' };
}
this.db.beginTransaction()
this.isInTransaction = true;
return {};
}
/**
* 提交事务
* 提交当前事务的所有操作
* @returns SQLiteResult 事务提交结果
*/
commit(): SQLiteResult {
if (!this.db) return {error:'Database not initialized'}
if (!this.isInTransaction) {
return { error: 'No transaction in progress' };
}
this.db.commit()
this.isInTransaction = false;
return {};
}
/**
* 回滚事务
* 撤销当前事务中的所有操作
* @returns SQLiteResult 事务回滚结果
*/
rollback(): SQLiteResult {
if (!this.db) return {error:'Database not initialized'}
if (!this.isInTransaction) {
return { error: 'No transaction in progress' };
}
this.db.rollBack()
this.isInTransaction = false;
return {};
}
/**
* 批量执行SQL语句
* @param statements SQL语句数组,每个元素包含sql和params
* @returns SQLiteResult[] 每个语句的执行结果数组
*/
executeBatch(statements: SQLiteExecuteBatchParams[]): SQLiteResult[] {
if (this.isInTransaction) {
return [];
}
const results: SQLiteResult[] = [];
let hasError = false;
// 自动使用事务包装批量操作
const wasInTransaction = this.isInTransaction;
if (!wasInTransaction) {
this.beginTransaction();
}
try {
for (const stmt of statements) {
const result = this.run(stmt.sql, stmt?.params??[]);
results.push(result);
if (result.error) {
hasError = true;
break;
}
}
// 如果有错误并且是我们开始的事务,则回滚
if (hasError && !wasInTransaction) {
this.rollback();
} else if (!wasInTransaction) {
// 如果是我们开始的事务且没有错误,则提交
this.commit();
}
} catch (error) {
// 发生异常且是我们开始的事务,则回滚
if (!wasInTransaction) {
this.rollback();
}
results.push({ error: error.message } as SQLiteResult);
}
return results;
}
/**
* 获取数据库文件路径
* 获取当前数据库文件的完整路径,获取前会备份到缓存目录
* @returns string|null 数据库文件路径,如果数据库未初始化则返回null
*/
getDatabasePath():string|null{
if (this.db==null){
console.error('Database not initialized')
return null
}
return '';
}
/**
* 当前数据库的目录
*/
setDefaultDirectory(directory: string){
console.warn('harmoney不支持本函数')
}
/**
* 设置数据库密码
* 空值或者null即删除密码
*/
setPassword(password: string|null = null){
console.warn('harmoney不支持本函数')
}
/**
* 删除数据表
* @param tableName 要删除的表名
* @returns SQLiteResult 删除结果
*/
dropTable(tableName: string): SQLiteResult {
const sql = `DROP TABLE IF EXISTS ${tableName}`;
return this.run(sql)
}
/**
* 查询数据
* @param sql 查询SQL语句
* @param params 查询参数数组
* @returns SQLiteResult 查询结果,包含rows(数据行)和columns(列名)
*/
query(sql: string, params: any[] = []): SQLiteResult {
if (this.db==null){
console.error('Database not initialized')
return {error:'Database not initialized'}
}
try {
if (this.isInTransaction) {
return { error: 'Transaction already in progress' };
}
const resultSet = this.db.querySqlSync(sql, params as relationalStore.ValueType[]);
let maps = [] as Map<string,any>[];
let rows:any[][] = [] ;
let columns:string[] =[] ;
while (resultSet.goToNextRow()) {
let newmap = new Map<string,any>();
let newrows:any[] = []
columns = resultSet.columnNames;
resultSet.columnNames.forEach((key:string,index:number)=>{
newmap.set(key,resultSet.getValue(index) as any)
newrows.push(resultSet.getValue(index) as any)
})
maps.push(newmap)
rows.push(newrows)
}
return {
rows: rows,
columns: columns,
maps
};
} catch (error) {
return { error: error.message };
}
}
/**
* 插入数据
* @param table 表名
* @param data 要插入的数据对象
* @returns SQLiteResult 插入结果
*/
insert(table: string, data: UTSJSONObject): SQLiteResult {
if (this.db==null){
return {error:'Database not initialized'};
}
let values:ValuesBucket = {};
data.toMap().forEach((value:any,key:string)=>{
let keyvalue = value as ValueType;
values[key as string] = keyvalue
})
const result = this.db.insertSync(table,values)
const re = result==-1?this.converFormat({error:"操作失败"} as SQLiteResult):(this.converFormat({lastInsertRowid:result} as SQLiteResult))
console.log(re)
return re
}
/**
* 创建数据表
* @param tableName 表名
* @param columns 列定义对象,key为列名,value为列类型定义
* @returns SQLiteResult 创建结果
*/
createTable(tableName: string, columns: UTSJSONObject): SQLiteResult {
const dataMap:string[] = [];
columns.toMap().forEach((value:any,key:string)=>{
dataMap.push(`${key} ${value}`)
})
const columnDefinitions = dataMap.join(',');
const sql = `CREATE TABLE IF NOT EXISTS ${tableName} (${columnDefinitions})`;
return this.run(sql);
}
/**
* 更新数据
* @param table 表名
* @param data 要更新的数据对象
* @param where WHERE条件语句
* @param params WHERE条件参数数组
* @returns SQLiteResult 更新结果
*/
update(table: string, data: UTSJSONObject, where: string, params: any[] = []): SQLiteResult {
if (this.db==null){
return {error:'Database not initialized'};
}
let keysold:string[] = []
let oldValues:any[] = []
data.toMap().forEach((value:any,key:string)=>{
keysold.push(key)
oldValues.push(value)
})
const sets = keysold.map(key => `${key} = ?`).join(',');
const values = [...oldValues, ...params];
const sql = `UPDATE ${table} SET ${sets} WHERE ${where}`;
return this.run(sql, values);
}
/**
* 删除数据
* @param table 表名
* @param where WHERE条件语句
* @param params WHERE条件参数数组
* @returns SQLiteResult 删除结果
*/
delete(table: string, where: string, params: any[] = []): SQLiteResult {
const sql = `DELETE FROM ${table} WHERE ${where}`;
return this.run(sql, params);
}
/**
* 检查表是否存在
* @param tableName 表名
* @returns boolean 表存在返回true,不存在返回false
*/
tableExists(tableName: string): boolean {
const result = this.query("SELECT name FROM sqlite_master WHERE type='table' AND name=?", [tableName]);
let rows = result?.rows?.length??0
return rows > 0;
}
}
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSPrivacyAccessedAPITypes</key>
<array/>
<key>NSPrivacyCollectedDataTypes</key>
<array/>
<key>NSPrivacyTracking</key>
<false/>
<key>NSPrivacyTrackingDomains</key>
<array/>
</dict>
</plist>
@@ -0,0 +1,3 @@
{
"deploymentTarget": "12"
}
@@ -0,0 +1,358 @@
import {SQLiteConfig,SQLiteResult,SQLiteExecuteBatchParams,SQLiteDbFileInfo} from '../interface.uts';
import { URL, URLResourceKey } from 'Foundation';
import { Attribute } from 'UIKit';
/**
* SQLite数据库操作类
* 提供SQLite数据库的创建、查询、更新等基本操作,支持加密和事务处理
*/
export class xSqlite {
/** 数据库配置信息 */
config : SQLiteConfig = {
password : null,
encryption : false,
locateFile:null
};
private db : xSqliteHelp|null = null;
constructor(config ?: SQLiteConfig ) {
if(config!=null){
let realCofing:SQLiteConfig = config!;
let directory:null|string = realCofing.defaultDirectory
if(typeof directory == 'string' && directory !=null){
directory = UTSiOS.convert2AbsFullPath(directory!)
}
realCofing.defaultDirectory = directory
this.config = realCofing;
}
this.db = new xSqliteHelp(
config = SQLiteConfigBySt(
password = this.config.password,
encryption = this.config.encryption,
defaultDirectory = this.config.defaultDirectory,
)
)
}
private converFormat(result?:SQLiteResultBySt):SQLiteResult{
if(result==null){
return {
rows: [] ,
columns: [] ,
changes: null,
lastInsertRowid: null,
error: null,
};
}
return {
rows: (result?.rows==null?[]:result?.rows),
columns: (result?.columns==null?[]:result?.columns),
changes: (result?.changes==null?null:result?.changes),
maps: (result?.maps==null?null:result?.maps),
lastInsertRowid: (result?.lastInsertRowid==null?null:result?.lastInsertRowid),
error: (result?.error==null?null:result?.error),
};
}
/**
* 创建数据库
* @param fileName 数据库文件名,如果为null则使用默认名称'sqlite'
* @returns 创建成功返回true,失败返回false
*/
createDb(fileName:string|null = null) : boolean{
if (this.db==null){
return false;
}
this.db!.createDb(filename = fileName)
return true
}
/**
* 执行SQL语句
* @param sql SQL语句
* @param params SQL参数数组
* @returns SQLiteResult 执行结果,包含changes(影响行数)和lastInsertRowid(最后插入行ID)
*/
run(sql: string, params: any[] = []): SQLiteResult {
try {
if (this.db==null) throw new Error('Database not initialized');
const result = this.db!.run(sql, params = params);
return this.converFormat(result)
} catch (error) {
return { error: error.message };
}
}
/**
* 查询数据
* @param sql 查询SQL语句
* @param params 查询参数数组
* @returns SQLiteResult 查询结果,包含rows(数据行)和columns(列名)
*/
query(sql: string, params: Array<any> = [] as any[]): SQLiteResult {
if (this.db==null){
console.error('Database not initialized')
return { error:"Database not initialized" };
}
const result = this.db!.query(sql, params = params);
return this.converFormat(result)
}
/**
* 插入数据
* @param table 表名
* @param data 要插入的数据对象
* @returns SQLiteResult 插入结果
*/
insert(table: string, data: UTSJSONObject): SQLiteResult {
if (this.db==null){
console.error('Database not initialized')
return { error:"Database not initialized" };
}
const dataMap = new Map<string,any>();
for(const key in data){
const item = data.getAny(key);
dataMap.set(key,item)
}
const result = this.db!.insert(table = table, data = dataMap);
return this.converFormat(result)
}
/**
* 更新数据
* @param table 表名
* @param data 要更新的数据对象
* @param whereName WHERE条件语句
* @param params WHERE条件参数数组
* @returns SQLiteResult 更新结果
*/
update(table: string, data: UTSJSONObject, whereName: string, params: any[] = []): SQLiteResult {
if (this.db==null){
console.error('Database not initialized')
return { error:"Database not initialized" };
}
const dataMap = new Map<string,any>();
for(const key in data){
const item = data.getAny(key);
dataMap.set(key,item)
}
const result = this.db!.update(table = table, data = dataMap, where = whereName, params = params);
return this.converFormat(result)
}
/**
* 删除数据
* @param table 表名
* @param whereName WHERE条件语句
* @param params WHERE条件参数数组
* @returns SQLiteResult 删除结果
*/
delete(table: string, whereName: string, params: any[] = []): SQLiteResult {
if (this.db==null){
console.error('Database not initialized')
return { error:"Database not initialized" };
}
const result = this.db!.delete(table = table, where = whereName ,params = params);
return this.converFormat(result)
}
/**
* 检查表是否存在
* @param tableName 表名
* @returns 表存在返回true,不存在返回false
*/
tableExists(tableName: string): boolean {
if (this.db==null){
console.error('Database not initialized')
return false;
}
return this.db!.tableExists(tableName = tableName);
}
/**
* 创建数据表
* @param tableName 表名
* @param columns 列定义对象,key为列名,value为列类型定义
* @returns SQLiteResult 创建结果
*/
createTable(tableName: string, columns: UTSJSONObject): SQLiteResult {
const dataMap = new Map<string,string>();
for(const key in columns){
const item = columns.getString(key)!;
dataMap.set(key,item)
}
console.log('createTabled:',this.db)
if (this.db==null){
console.error('Database not initialized')
return { error:"Database not initialized" };
}
try {
const result = this.db!.createTable(tableName = tableName, columns = dataMap);
return this.converFormat(result)
} catch (error) {
console.error(error)
return { error: error.message };
}
}
/**
* 删除数据表
* @param tableName 要删除的表名
* @returns SQLiteResult 删除结果
*/
dropTable(tableName: string): SQLiteResult {
if (this.db==null){
console.error('Database not initialized')
return { error:"Database not initialized" };
}
const result = this.db!.dropTable(tableName = tableName);
return this.converFormat(result)
}
/**
* 关闭数据库连接
* 释放数据库资源,关闭后需要重新创建才能使用
*/
close(): void {
if (this.db!=null) {
this.db!.close();
this.db = null;
}
}
/**
* 开始事务
* 开始一个新的事务,在提交或回滚之前,所有操作都在事务内
* @returns SQLiteResult 事务开始结果
*/
beginTransaction(): SQLiteResult{
if (this.db==null){
console.error('Database not initialized')
return { error:"Database not initialized" };
}
const result = this.db!.beginTransaction();
return this.converFormat(result)
}
/**
* 提交事务
* 提交当前事务的所有操作
* @returns SQLiteResult 事务提交结果
*/
commit(): SQLiteResult {
if (this.db==null){
console.error('Database not initialized')
return { error:"Database not initialized" };
}
const result = this.db!.commit();
return this.converFormat(result)
}
/**
* 回滚事务
* 撤销当前事务中的所有操作
* @returns SQLiteResult 事务回滚结果
*/
rollback(): SQLiteResult {
if (this.db==null){
console.error('Database not initialized')
return { error:"Database not initialized" };
}
const result = this.db!.rollback();
return this.converFormat(result)
}
/**
* 批量执行SQL语句
* @param statements SQL语句数组,每个元素包含sql和params
* @returns SQLiteResult[] 每个语句的执行结果数组
*/
executeBatch(statements: SQLiteExecuteBatchParams[]): SQLiteResult[] {
if (this.db==null){
console.error('Database not initialized')
return [];
}
const list = new Array<SQLiteExecuteBatchParamsBySt>()
for(const item in statements){
list.push(SQLiteExecuteBatchParamsBySt(
sql = item.sql,
params = item.params
))
}
const result = this.db!.executeBatch(statements = list);
let resultList = [] as SQLiteResult[]
for(const item in result){
resultList.push(this.converFormat(item))
}
return resultList
}
/**
* 保存数据库到本地文件
* @param filename 保存的文件名,可选,默认为'sqlite'
* @returns SQLiteResult 保存结果
*/
saveLocal(filename?: string): SQLiteResult {
if (this.db==null){
return {error:'Database not initialized'};
}
const result = this.db!.saveLocal(filename = filename);
return this.converFormat(result)
}
/**
* 从本地文件加载数据库
* @param filename 要加载的数据库文件名
* @returns SQLiteResult 加载结果
*/
loadLocal(filename:string): SQLiteResult{
const result = this.db!.loadLocal(filename = filename);
return this.converFormat(result)
}
/**
* 获取数据库文件路径获取前会备份到缓存目录
*/
getDatabasePath():string|null{
if (this.db==null){
console.error('Database not initialized')
return null
}
const result = this.db!.getDatabasePath();
return result
}
/**
* 当前数据库的目录
*/
setDefaultDirectory(directory: string){
if (this.db==null){
console.error('Database not initialized')
return
}
let rdirectory = directory;
if(rdirectory!=''){
rdirectory = UTSiOS.convert2AbsFullPath(directory)
}
this.db!.setDefaultDirectory(directory = rdirectory);
}
/**
* 设置数据库密码
* 空值或者null即删除密码
*/
setPassword(password: string|null = null){
if (this.db==null){
console.error('Database not initialized')
return
}
this.db!.setPassword(password);
}
}
@@ -0,0 +1,522 @@
import Foundation
import SQLite3
// UTS
import DCloudUTSFoundation
struct SQLiteConfigBySt {
var password: String?
var encryption: Bool
var locateFile: ((String) -> String)?
var defaultDirectory: String?
init(password: String? = nil, encryption: Bool = false, locateFile: ((String) -> String)? = nil, defaultDirectory: String? = nil) {
self.password = password
self.encryption = encryption
self.locateFile = locateFile
self.defaultDirectory = defaultDirectory
}
}
struct SQLiteResultBySt {
var rows: [[Any]]?
var columns: [String]?
var changes: Int?
var lastInsertRowid: Int64?
var error: String?
var maps: [[String: Any]]?
}
struct SQLiteExecuteBatchParamsBySt {
var sql: String
var params: [Any]?
}
class xSqliteHelp {
private var db: OpaquePointer?
private var isInTransaction: Bool = false
private var config: SQLiteConfigBySt
private var fileURLPath: URL?
init(config: SQLiteConfigBySt = SQLiteConfigBySt()) {
self.config = config
}
//
func createDb(filename: String? = nil) -> OpaquePointer? {
let fname = filename ?? "sqlite"
let fileURL: URL
if let defaultDir = config.defaultDirectory {
fileURL = URL(fileURLWithPath: defaultDir).appendingPathComponent("\(fname).db")
//
let dirURL = fileURL.deletingLastPathComponent()
let fileManager = FileManager.default
if !fileManager.fileExists(atPath: dirURL.path) {
do {
try fileManager.createDirectory(at: dirURL, withIntermediateDirectories: true, attributes: nil)
} catch {
// print(": \(error)")
return nil
}
}
} else {
fileURL = try! FileManager.default
.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
.appendingPathComponent("\(fname).db")
}
var dbPointer: OpaquePointer?
if sqlite3_open(fileURL.path, &dbPointer) == SQLITE_OK {
db = dbPointer
if config.encryption && config.password != nil {
_ = run("PRAGMA key = ?", params: [config.password!])
}
self.fileURLPath = fileURL
return db
} else {
sqlite3_close(dbPointer)
return nil
}
}
// SQL
func run(_ sql: String, params: [Any] = []) -> SQLiteResultBySt {
guard let db = db else {
return SQLiteResultBySt(error: "Database not initialized")
}
var statement: OpaquePointer?
if sqlite3_prepare_v2(db, sql, -1, &statement, nil) != SQLITE_OK {
let errmsg = String(cString: sqlite3_errmsg(db))
return SQLiteResultBySt(error: errmsg)
}
for (index, param) in params.enumerated() {
let idx = Int32(index + 1)
switch param {
case let text as String:
sqlite3_bind_text(statement, idx, (text as NSString).utf8String, -1, nil)
case let num as Int:
sqlite3_bind_int64(statement, idx, Int64(num))
case let num as Int64:
sqlite3_bind_int64(statement, idx, num)
case let num as Double:
sqlite3_bind_double(statement, idx, num)
case let data as Data:
data.withUnsafeBytes { bytes in
sqlite3_bind_blob(statement, idx, bytes.baseAddress, Int32(data.count), nil)
}
case is NSNull:
sqlite3_bind_null(statement, idx)
default:
"\(param)".utf8CString.withUnsafeBufferPointer { cString in
sqlite3_bind_text(statement, idx, cString.baseAddress, -1, nil)
}
}
}
let result = sqlite3_step(statement)
if result != SQLITE_DONE && result != SQLITE_ROW {
let errmsg = String(cString: sqlite3_errmsg(db))
sqlite3_finalize(statement)
return SQLiteResultBySt(error: errmsg)
}
let changes = sqlite3_changes(db)
let lastId = sqlite3_last_insert_rowid(db)
sqlite3_finalize(statement)
return SQLiteResultBySt(changes: Int(changes), lastInsertRowid: lastId)
}
//
func query(_ sql: String, params: [Any] = []) -> SQLiteResultBySt {
guard let db = db else {
return SQLiteResultBySt(error: "Database not initialized")
}
var statement: OpaquePointer?
if sqlite3_prepare_v2(db, sql, -1, &statement, nil) != SQLITE_OK {
let errmsg = String(cString: sqlite3_errmsg(db))
return SQLiteResultBySt(error: errmsg)
}
for (index, param) in params.enumerated() {
let idx = Int32(index + 1)
switch param {
case let text as String:
sqlite3_bind_text(statement, idx, (text as NSString).utf8String, -1, nil)
case let num as Int:
sqlite3_bind_int64(statement, idx, Int64(num))
case let num as Int64:
sqlite3_bind_int64(statement, idx, num)
case let num as Double:
sqlite3_bind_double(statement, idx, num)
case let data as Data:
data.withUnsafeBytes { bytes in
sqlite3_bind_blob(statement, idx, bytes.baseAddress, Int32(data.count), nil)
}
case is NSNull:
sqlite3_bind_null(statement, idx)
default:
"\(param)".utf8CString.withUnsafeBufferPointer { cString in
sqlite3_bind_text(statement, idx, cString.baseAddress, -1, nil)
}
}
}
var rows: [[Any]] = []
var columns: [String] = []
var maps: [[String: Any]] = []
//
let columnCount = sqlite3_column_count(statement)
for i in 0..<columnCount {
if let name = sqlite3_column_name(statement, i) {
columns.append(String(cString: name))
}
}
//
while sqlite3_step(statement) == SQLITE_ROW {
var row: [Any] = []
var map: [String: Any] = [:]
for i in 0..<columnCount {
let type = sqlite3_column_type(statement, i)
let columnName = columns[Int(i)]
var value: Any = ""
switch type {
case SQLITE_TEXT:
if let text = sqlite3_column_text(statement, i) {
value = String(cString: text)
}
case SQLITE_INTEGER:
value = sqlite3_column_int64(statement, i)
case SQLITE_FLOAT:
value = sqlite3_column_double(statement, i)
case SQLITE_BLOB:
let blobLength = sqlite3_column_bytes(statement, i)
if let blobData = sqlite3_column_blob(statement, i) {
value = Data(bytes: blobData, count: Int(blobLength))
} else {
value = Data()
}
default:
value = ""
}
row.append(value)
map[columnName] = value
}
rows.append(row)
maps.append(map)
}
sqlite3_finalize(statement)
return SQLiteResultBySt(rows: rows, columns: columns, maps: maps)
}
//
func insert(table: String, data: [String: Any]) -> SQLiteResultBySt {
let keys = Array(data.keys)
let values = keys.map { data[$0]! }
let sql = "INSERT INTO \(table) (\(keys.joined(separator: ","))) VALUES (\(keys.map { _ in "?" }.joined(separator: ",")))"
return run(sql, params: values)
}
//
func update(table: String, data: [String: Any], where: String, params: [Any] = []) -> SQLiteResultBySt {
let keys = Array(data.keys)
var values = keys.map { data[$0]! }
let sets = keys.map { "\($0) = ?" }.joined(separator: ",")
values.append(contentsOf: params)
let sql = "UPDATE \(table) SET \(sets) WHERE \(`where`)"
return run(sql, params: values)
}
//
func delete(table: String, where: String, params: [Any] = []) -> SQLiteResultBySt {
let sql = "DELETE FROM \(table) WHERE \(`where`)"
return run(sql, params: params)
}
//
func saveLocal(filename: String? = nil) -> SQLiteResultBySt {
guard let db = self.db else {
return SQLiteResultBySt(error: "Database not initialized")
}
do {
let fname = filename ?? "sqlite"
let fileManager = FileManager.default
// 使
let targetDir = self.config.defaultDirectory != nil ? URL(fileURLWithPath: self.config.defaultDirectory!) : fileManager.urls(for: .documentDirectory, in: .userDomainMask)[0]
//
var dbPath = String(cString: sqlite3_db_filename(self.db!, nil))
// dbPath使fileURLPath
if dbPath.isEmpty {
if let fileURL = self.fileURLPath {
dbPath = fileURL.path
} else {
return SQLiteResultBySt(error: "无法获取数据库文件路径")
}
}
let sourceUrl = URL(fileURLWithPath: dbPath)
let targetUrl = targetDir.appendingPathComponent("\(fname).db")
//
if !fileManager.fileExists(atPath: sourceUrl.path) {
return SQLiteResultBySt(error: "未能找到数据库文件")
}
guard let sourceAttrs = try? fileManager.attributesOfItem(atPath: sourceUrl.path),
sourceAttrs[.size] as? UInt64 ?? 0 > 0 else {
return SQLiteResultBySt(error: "数据库文件无效或损坏")
}
//
sqlite3_exec(db, "PRAGMA wal_checkpoint(FULL)", nil, nil, nil)
//
let tempUrl = targetDir.appendingPathComponent("\(fname)_temp.db")
//
if fileManager.fileExists(atPath: tempUrl.path) {
try fileManager.removeItem(at: tempUrl)
}
//
try fileManager.copyItem(at: sourceUrl, to: tempUrl)
//
guard let tempAttrs = try? fileManager.attributesOfItem(atPath: tempUrl.path),
tempAttrs[.size] as? UInt64 ?? 0 > 0 else {
try? fileManager.removeItem(at: tempUrl)
return SQLiteResultBySt(error: "备份文件创建失败")
}
//
if fileManager.fileExists(atPath: targetUrl.path) {
try fileManager.removeItem(at: targetUrl)
}
//
try fileManager.moveItem(at: tempUrl, to: targetUrl)
return SQLiteResultBySt()
} catch {
return SQLiteResultBySt(error: error.localizedDescription)
}
}
//
func loadLocal(filename: String) -> SQLiteResultBySt {
let fileManager = FileManager.default
let sourceDir = self.config.defaultDirectory != nil ? URL(fileURLWithPath: self.config.defaultDirectory!) : fileManager.urls(for: .documentDirectory, in: .userDomainMask)[0]
let sourceUrl = sourceDir.appendingPathComponent("\(filename).db")
let targetUrl = sourceDir.appendingPathComponent("sqlite.db")
do {
//
if !fileManager.fileExists(atPath: sourceUrl.path) {
return SQLiteResultBySt(error: "没有数据库")
}
guard let sourceAttrs = try? fileManager.attributesOfItem(atPath: sourceUrl.path),
sourceAttrs[.size] as? UInt64 ?? 0 > 0 else {
return SQLiteResultBySt(error: "数据库文件无效或损坏")
}
//
let tempUrl = sourceDir.appendingPathComponent("\(filename)_temp.db")
//
if fileManager.fileExists(atPath: tempUrl.path) {
try fileManager.removeItem(at: tempUrl)
}
//
try fileManager.copyItem(at: sourceUrl, to: tempUrl)
//
guard let tempAttrs = try? fileManager.attributesOfItem(atPath: tempUrl.path),
tempAttrs[.size] as? UInt64 ?? 0 > 0 else {
try? fileManager.removeItem(at: tempUrl)
return SQLiteResultBySt(error: "数据库文件损坏")
}
//
if fileManager.fileExists(atPath: targetUrl.path) {
try fileManager.removeItem(at: targetUrl)
}
//
try fileManager.moveItem(at: tempUrl, to: targetUrl)
//
if let _ = self.createDb() {
return SQLiteResultBySt()
} else {
return SQLiteResultBySt(error: "数据库文件损坏")
}
} catch {
//
let tempUrl = sourceDir.appendingPathComponent("\(filename)_temp.db")
try? fileManager.removeItem(at: tempUrl)
return SQLiteResultBySt(error: "数据库文件损坏")
}
}
//
func setDefaultDirectory(directory: String) {
self.config.defaultDirectory = directory
}
//
func setPassword(_ password: String?) {
config.password = password
config.encryption = password != nil && !password!.isEmpty
}
//
func tableExists(tableName: String) -> Bool {
let result = query("SELECT name FROM sqlite_master WHERE type='table' AND name=?", params: [tableName])
return (result.rows?.isEmpty == false)
}
//
func createTable(tableName: String, columns: [String: String]) -> SQLiteResultBySt {
let columnDefinitions = columns.map { key, value in "\(key) \(value)" }.joined(separator: ",")
let sql = "CREATE TABLE IF NOT EXISTS \(tableName) (\(columnDefinitions))"
return run(sql)
}
//
func dropTable(tableName: String) -> SQLiteResultBySt {
let sql = "DROP TABLE IF EXISTS \(tableName)"
return run(sql)
}
//
func close() {
if let db = db {
sqlite3_close(db)
self.db = nil
}
}
//
func beginTransaction() -> SQLiteResultBySt {
if isInTransaction {
return SQLiteResultBySt(error: "Transaction already in progress")
}
isInTransaction = true
return run("BEGIN TRANSACTION")
}
//
func commit() -> SQLiteResultBySt {
if !isInTransaction {
return SQLiteResultBySt(error: "No transaction in progress")
}
isInTransaction = false
return run("COMMIT")
}
//
func rollback() -> SQLiteResultBySt {
if !isInTransaction {
return SQLiteResultBySt(error: "No transaction in progress")
}
isInTransaction = false
return run("ROLLBACK")
}
// SQL
func executeBatch(statements: [SQLiteExecuteBatchParamsBySt]) -> [SQLiteResultBySt] {
var results: [SQLiteResultBySt] = []
var hasError = false
let wasInTransaction = isInTransaction
if !wasInTransaction {
_ = beginTransaction()
}
for stmt in statements {
let result = run(stmt.sql, params: stmt.params ?? [])
results.append(result)
if result.error != nil {
hasError = true
break
}
}
if hasError && !wasInTransaction {
_ = rollback()
} else if !wasInTransaction {
_ = commit()
}
return results
}
//
func getDatabasePath() -> String? {
let fileManager = FileManager.default
guard let db = self.db else {
return nil
}
do {
//
var dbPath = String(cString: sqlite3_db_filename(self.db!, nil))
// dbPath使fileURLPath
if dbPath.isEmpty {
if let fileURL = self.fileURLPath {
dbPath = fileURL.path
} else {
return nil
}
}
//
let cacheURL = try FileManager.default.url(for: .cachesDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
let tempURL = cacheURL.appendingPathComponent("tmui4x_xSqlite_DbackTemp.db")
//
if fileManager.fileExists(atPath: tempURL.path) {
try fileManager.removeItem(at: tempURL)
}
//
try FileManager.default.copyItem(atPath: dbPath, toPath: tempURL.path)
return tempURL.path
} catch {
return nil
}
}
}
@@ -0,0 +1,23 @@
export type SQLiteConfig = {
password?: string;
encryption?: boolean;
locateFile?: (filename: string) => string;
defaultDirectory?: string;
}
export type SQLiteResult = {
rows?: any[][];
columns?: string[];
changes?: number;
lastInsertRowid?: number;
error?: string;
maps?:Map<string,any>[]
}
export type SQLiteExecuteBatchParams = {
sql:string,
params?:any[]
}
export type SQLiteDbFileInfo = {
path:string|null,
size:number|null
}
+481
View File
@@ -0,0 +1,481 @@
export type SQLiteConfig = {
password?: string;
encryption?: boolean;
locateFile?: (filename: string) => string;
}
export type SQLiteResult = {
rows?: any[];
columns?: string[];
changes?: number;
lastInsertRowid?: number;
error?: string;
maps?:Map<string,any>[]
}
export type SQLiteExecuteBatchParams = {
sql:string,
params?:any[]
}
async function loadScript(url) {
return new Promise(res => {
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = url;
// 当脚本加载完成后,执行回调
script.onload = function () {
res()
};
// 处理旧版浏览器的onreadystatechange事件
script.onreadystatechange = function () {
if (this.readyState === 'loaded' || this.readyState === 'complete') {
script.onload();
}
};
// 将脚本添加到head中,开始加载
document.head.appendChild(script);
})
}
async function loadScripts(scripts) {
await (async function loadNextScript(i) {
if (i < scripts.length) {
await loadScript(scripts[i]);
await loadNextScript(i + 1)
}
})(0);
}
let jsFiles = [
'/static/tmui4xLibs/lib/sql-wasm.js'
];
/**
* SQLite数据库操作类
* 提供SQLite数据库的创建、查询、更新等基本操作,支持加密和事务处理
*/
export class xSqlite {
/** 数据库实例 */
private db: any|null = null;
private config: SQLiteConfig;
private isInTransaction: boolean = false;
private SQL:any|null = null;
private newFileName:string = "sqlite"
constructor(configs: SQLiteConfig = {}) {
let locateFile = filename => `/static/tmui4xLibs/lib/sql-wasm.wasm`
this.config = {locateFile,...configs};
this.config.locateFile = this.config.locateFile||locateFile
}
private _loadSqliteJs(): Promise<any>{
let _this = this;
return new Promise((res, rej) => {
loadScripts(jsFiles).then(() => {
initSqlJs(_this.config).then((SQL) => {
_this.SQL = SQL;
res();
}).catch(e => rej(e))
}).catch(e => rej(e))
})
}
/**
* 创建数据库
* @param filename 数据库文件名,如果为null则使用默认名称'sqlite'
* @returns Promise<any> 创建的数据库实例
*/
async createDb(filename?:string): Promise<any> {
let _this = this;
this.newFileName = filename||"sqlite"
return new Promise((res, rej) => {
_this._loadSqliteJs()
.then(_=>{
_this.db = new _this.SQL.Database();
// 如果配置了加密
if (this.config.encryption && _this.config.password) {
_this.run('PRAGMA key = ?', [_this.config.password]);
}
})
.catch(_=>{
})
})
}
/**
* 执行SQL语句
* @param sql SQL语句
* @param params SQL参数数组
* @returns SQLiteResult 执行结果,包含changes(影响行数)和lastInsertRowid(最后插入行ID)
*/
run(sql: string, params: any[] = []): SQLiteResult {
try {
if (this.isInTransaction) {
return { error: 'Transaction already in progress' };
}
const result = this.db.run(sql, params);
return {
changes: result?.changes,
lastInsertRowid: result?.lastInsertRowid
};
} catch (error) {
return { error: error.message };
}
}
/**
* 查询数据
* @param sql 查询SQL语句
* @param params 查询参数数组
* @returns SQLiteResult 查询结果,包含rows(数据行)和columns(列名)
*/
query(sql: string, params: any[] = []): SQLiteResult {
try {
if (this.isInTransaction) {
return { error: 'Transaction already in progress' };
}
const result = this.db.exec(sql, params);
const maps = [] as Map<string,any>[];
const rows = result?.[0]?.values || [] ;
const columns = result?.[0]?.columns || [] ;
if(rows.length>0&&columns.length>0){
rows.forEach(el=>{
let newmap = new Map<string,any>();
columns.forEach((key,index)=>{
newmap.set(key,el[index])
})
maps.push(newmap)
})
}
return {
rows: rows,
columns: columns,
maps
};
} catch (error) {
return { error: error.message };
}
}
/**
* 插入数据
* @param table 表名
* @param data 要插入的数据对象
* @returns SQLiteResult 插入结果
*/
insert(table: string, data: object): SQLiteResult {
const keys = Object.keys(data);
const values = Object.values(data);
const sql = `INSERT INTO ${table} (${keys.join(',')}) VALUES (${keys.map(() => '?').join(',')})`;
return this.run(sql, values);
}
/**
* 更新数据
* @param table 表名
* @param data 要更新的数据对象
* @param where WHERE条件语句
* @param params WHERE条件参数数组
* @returns SQLiteResult 更新结果
*/
update(table: string, data: object, where: string, params: any[] = []): SQLiteResult {
const sets = Object.keys(data).map(key => `${key} = ?`).join(',');
const values = [...Object.values(data), ...params];
const sql = `UPDATE ${table} SET ${sets} WHERE ${where}`;
return this.run(sql, values);
}
/**
* 删除数据
* @param table 表名
* @param where WHERE条件语句
* @param params WHERE条件参数数组
* @returns SQLiteResult 删除结果
*/
delete(table: string, where: string, params: any[] = []): SQLiteResult {
const sql = `DELETE FROM ${table} WHERE ${where}`;
return this.run(sql, params);
}
/**
* 保存数据库到本地存储
* @param filename 保存的文件名,可选,默认为'sqlite'
* @returns SQLiteResult 保存结果
*/
saveLocal(filename?: string): SQLiteResult {
if (this.db==null) throw new Error('Database not initialized');
let fname = filename??'sqlite'
const data = this.db.export();
this.newFileName = fname;
// const blob = new Blob([data], { type: 'application/octet-stream' });
// const url = window.URL.createObjectURL(blob);
const base64String = btoa(String.fromCharCode(...data));
// const a = document.createElement('a');
// a.href = url;
// a.download = filename;
// document.body.appendChild(a);
// a.click();
// a.remove();
// window.URL.revokeObjectURL(url);
if(base64String.length>5*1024*1024){
console.error("数据过大")
return {error:"数据过大"};
}
try {
uni.setStorageSync(fname,base64String)
} catch (error) {
return {error:"数据过大"};
}
}
async loadLocal(filename: string): Promise<SQLiteResult> {
this.newFileName = filename;
const data = uni.getStorageSync(filename)
if(data==null){
console.error("没有数据库")
return Promise.reject({error:"没有数据库"})
}
try {
const buffer = Uint8Array.from(atob(data), c => c.charCodeAt(0));
await this._loadSqliteJs()
this.loadFromBuffer(buffer);
return Promise.resolve({})
} catch (error) {
return Promise.reject({error:"数据库损坏"})
}
return Promise.reject({error:"没有数据库"})
}
/**
* 从文件加载数据库
* @param file 要加载的数据库文件
* @returns Promise<void> 加载完成的Promise
*/
async loadFromFile(file: File): Promise<void> {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = async event => {
try {
const uInt8Array = new Uint8Array(event.target.result as ArrayBuffer);
await this.loadFromBuffer(uInt8Array);
resolve();
} catch (error) {
reject(error);
}
};
reader.onerror = () => reject(reader.error);
reader.readAsArrayBuffer(file);
});
}
/**
* 从远程URL加载数据库
* @param url 数据库文件的URL地址
* @returns Promise<void> 加载完成的Promise
*/
async loadFromUrl(url: string): Promise<void> {
const response = await fetch(url);
if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
const buffer = await response.arrayBuffer();
await this.loadFromBuffer(new Uint8Array(buffer));
}
// 从Buffer加载数据库
loadFromBuffer(buffer: Uint8Array): void{
if (this.SQL==null) throw new Error('Database not initialized');
this.db = new this.SQL.Database(buffer);
// 如果配置了加密
if (this.config.encryption && this.config.password) {
this.run('PRAGMA key = ?', [this.config.password]);
}
}
/**
* 检查表是否存在
* @param tableName 表名
* @returns boolean 表存在返回true,不存在返回false
*/
tableExists(tableName: string): boolean {
const result = this.query("SELECT name FROM sqlite_master WHERE type='table' AND name=?", [tableName]);
return result.rows && result.rows.length > 0;
}
/**
* 创建数据表
* @param tableName 表名
* @param columns 列定义对象,key为列名,value为列类型定义
* @returns SQLiteResult 创建结果
*/
createTable(tableName: string, columns: UTSJSONObject): SQLiteResult {
const dataMap = new Map<string,string>();
for(const key in columns){
const item = columns.getString(key)!;
dataMap.set(key,item)
}
const columnDefinitions = Array.from(dataMap.entries())
.map(([name, type]) => `${name} ${type}`)
.join(',');
const sql = `CREATE TABLE IF NOT EXISTS ${tableName} (${columnDefinitions})`;
return this.run(sql);
}
/**
* 删除数据表
* @param tableName 要删除的表名
* @returns SQLiteResult 删除结果
*/
dropTable(tableName: string): SQLiteResult {
const sql = `DROP TABLE IF EXISTS ${tableName}`;
return this.run(sql);
}
/**
* 关闭数据库连接
* 释放数据库资源,关闭后需要重新创建才能使用
*/
close(): void {
if (this.db) {
this.db.close();
this.db = null;
}
}
/**
* 开始事务
* 开始一个新的事务,在提交或回滚之前,所有操作都在事务内
* @returns SQLiteResult 事务开始结果
*/
beginTransaction(): SQLiteResult{
if (this.isInTransaction) {
return { error: 'Transaction already in progress' };
}
const result = this.run('BEGIN TRANSACTION');
if (!result.error) {
this.isInTransaction = true;
}
return result;
}
/**
* 提交事务
* 提交当前事务的所有操作
* @returns SQLiteResult 事务提交结果
*/
commit(): SQLiteResult {
if (!this.isInTransaction) {
return { error: 'No transaction in progress' };
}
const result = this.run('COMMIT');
if (!result.error) {
this.isInTransaction = false;
}
return result;
}
/**
* 回滚事务
* 撤销当前事务中的所有操作
* @returns SQLiteResult 事务回滚结果
*/
rollback(): SQLiteResult {
if (!this.isInTransaction) {
return { error: 'No transaction in progress' };
}
const result = this.run('ROLLBACK');
if (!result.error) {
this.isInTransaction = false;
}
return result;
}
/**
* 批量执行SQL语句
* @param statements SQL语句数组,每个元素包含sql和params
* @returns SQLiteResult[] 每个语句的执行结果数组
*/
executeBatch(statements: SQLiteExecuteBatchParams[]): SQLiteResult[] {
if (this.isInTransaction) {
return [];
}
const results: SQLiteResult[] = [];
let hasError = false;
// 自动使用事务包装批量操作
const wasInTransaction = this.isInTransaction;
if (!wasInTransaction) {
this.beginTransaction();
}
try {
for (const stmt of statements) {
const result = this.run(stmt.sql, stmt?.params??[]);
results.push(result);
if (result.error) {
hasError = true;
break;
}
}
// 如果有错误并且是我们开始的事务,则回滚
if (hasError && !wasInTransaction) {
this.rollback();
} else if (!wasInTransaction) {
// 如果是我们开始的事务且没有错误,则提交
this.commit();
}
} catch (error) {
// 发生异常且是我们开始的事务,则回滚
if (!wasInTransaction) {
this.rollback();
}
results.push({ error: error.message });
}
return results;
}
/**
* 获取数据库文件路径
* 获取当前数据库文件的完整路径,获取前会备份到缓存目录
* @returns string|null 数据库文件路径,如果数据库未初始化则返回null
*/
getDatabasePath():Uint8Array|null{
if (this.db==null){
console.error('Database not initialized')
return null
}
const data = this.db.export();
// const blob = new Blob([data], { type: 'application/octet-stream' });
// const url = window.URL.createObjectURL(blob);
return data;
}
/**
* 当前数据库的目录
*/
setDefaultDirectory(directory: string){
if (this.db==null){
console.error('Database not initialized')
return
}
}
/**
* 设置数据库密码
* 空值或者null即删除密码
*/
setPassword(password: string|null = null){
if (this.db==null){
console.error('Database not initialized')
return
}
this.config.password = password
this.config.encryption = password!=null&&!!password
}
}