更新记录
1.0.9(2026-02-01)
修改了uniappX的最低版本
1.0.8(2026-02-01)
[优化] 更改了export方式,同时将interface.uts中的类型一并暴露 [修复] 删除了测试代码
1.0.7(2026-01-26)
优化了说明文档
查看更多平台兼容性
uni-app x(4.87)
| Chrome | Safari | Android | Android插件版本 | iOS | 鸿蒙 | 鸿蒙插件版本 | 微信小程序 |
|---|---|---|---|---|---|---|---|
| × | × | 5.0 | 1.0.9 | × | 6.0.0(20) | 1.0.9 | × |
其他
| 多语言 | 暗黑模式 | 宽屏模式 |
|---|---|---|
| √ | √ | √ |
SQLite 插件说明文档
一、插件概述
1.1 插件简介
- 该SQLite插件是基于UniApp X + UTS开发的本地数据库操作插件,封装了SQLite数据库的核心操作(连接管理、CRUD、事务、多表关联查询等),提供了 ORM面向对象的模型化开发方式,简化了原生SQLite的使用复杂度,同时保证了类型安全和操作规范性。
- 插件使用代码风格和API设计参考了Sequelize,旨在为前端出身的开发者提供一中学习成本低、快速上手的数据库操作方案。
- 平台适配性:支持在【Android】、【HarmonyNext】端APP中使用。
二、快速开始
2.1 安装与引入
通过HbuildX插件市场搜索【se-sqlite】安装插件:
import { SQLiteHelper } from '@/uni_modules/se-sqlite'
2.2 基础使用流程
步骤1:初始化数据库连接
// 初始化SQLiteHelper实例,参数为数据库名称(自动创建.db后缀)
// --------------------------------安卓端--------------------------------------
// 安卓端数据库的连接是同步操作,所以无需等待
const sqlite = new SQLiteHelper('demo');
// -------------------------------- 鸿蒙端 --------------------------------
// 鸿蒙端数据库的连接是异步操作,需要等待连接成功后才能执行后续操作
const sqlite = new SQLiteHelper('demo');
sqlite.open().then(() => {
// 检查连接状态
console.log(sqlite.isOpen()); // 输出:true
})
// 或者在异步函数内部使用
const sqlite = new SQLiteHelper('demo');
await sqlite.open();
// 检查连接状态
console.log(sqlite.isOpen()); // 输出:true
步骤2:定义数据模型
import { ModelDefType } from '@/uni_modules/se-sqlite'
-------------------------------- 基本示例 --------------------------------
const userModelDef : ModelDefType = {
tableName: 'user', // 表名
columns: [// 列定义
{ name: "id", type: "INTEGER", primaryKey: true, autoIncrement: true },
{ name: "name", type: "TEXT" },
{ name: "age", type: "INTEGER", default: 0 },
{ name: "createAt", type: "DATE", default: "NOW" }
]
};
-------------------------------- 外键示例 --------------------------------
export const houseModelDef : ModelDefType = {
tableName: 'house', // 表名
columns: [ // 列定义
{ name: "id", type: "INTEGER", primaryKey: true, autoIncrement: true },
{ name: "label", type: "TEXT" },
{ name: "ownerId", type: "INTEGER", notNull: true default: null },
{ name: "createAt", type: "DATE", default: "NOW" }
],
foreignKeys: [ // 外键定义
{
field: "ownerId", // 当前表的外键字段
refTable: "user", // 关联的主表
refField: "id", // 主表的关联字段
onDelete: "CASCADE" // 级联删除:用户删除时,关联房产也删除
}
]
};
-------------------------------- 唯一约束示例 --------------------------------
export const relUserHouseModelDef : ModelDefType = {
tableName: 'relUserHouse', // 表名
columns: [
{ name: "id", type: "INTEGER", primaryKey: true, autoIncrement: true },
{ name: "ownerId", type: "INTEGER", notNull: false },
{ name: "houseId", type: "INTEGER", notNull: false },
{ name: "createAt", type: "DATE", default: "NOW" }
],
foreignKeys: [ // 外键定义(多个)
{
field: "ownerId", // 当前表的外键字段
refTable: "user", // 关联的主表
refField: "id", // 主表的关联字段
onDelete: "CASCADE" // 级联删除:用户删除时,关联房产也删除
}, {
field: "houseId", // 当前表的外键字段
refTable: "house", // 关联的主表
refField: "id", // 主表的关联字段
onDelete: "CASCADE" // 级联删除:用户删除时,关联房产也删除
}
],
uniqueKeys: [
{
name: "uk_owner_house", // 可选:约束名称,便于后续维护
fields: ["ownerId", "houseId"] // 核心:这两个字段组合唯一
}
]
};
// 导出默认值(建议一张表一个文件,方便维护。也可遵循个人习惯。)
export default userModelDef;
// export default houseModelDef;
// export default relUserHouseModelDef;
步骤3:挂载模型并使用
// 挂载模型(自动创建表)
const UserModel = sqlite.defineModel(userModelDef);
// defineModel方法每次调用会解析数据模型,并使用 `CREATE TABLE IF NOT EXISTS 【表名】`自动创建表结构,如果表已存在,则不会重复创建
// 基础查询
const result = UserModel.query({
where: { age: { $gt: 18 } } // 查询年龄大于18的用户
});
console.log(result);
- 注意: 鸿蒙端需要注意调用defineModel()前先调用sqlite.open()方法并确保数据库连接成功,否则会报错。
2.3 更多示例
2.3.1 插入数据
const result = UserModel.insert({
name: '张三',
age: 20
});
console.log(result);
2.3.2 批量插入数据
const result = UserModel.insertBatch([
{ name: '李四', age: 22 },
{ name: '王五', age: 25 }
]);
console.log(result);
2.3.3 批量插入数据(开启事务)
// 此方法仅鸿蒙端可用
const result = UserModel.insertBatchWithTransaction([
{ name: '李四', age: 22 },
{ name: '王五', age: 25 }
]);
console.log(result);
2.3.4 更新数据
const result = UserModel.update({
name: '张三',
age: 21
}, {
where: { id: 1 } // 更新id为1的用户
});
console.log(result);
2.3.5 删除数据
const result = UserModel.delete({
where: { id: 1 } // 删除id为1的用户
});
console.log(result);
2.3.6 查询数据
const result = UserModel.find({
where: { age: { $gt: 18 } }, // 查询年龄大于18的用户
orderBy: { age: "DESC" }, // 按年龄倒序排列
limit: 10, // 限制返回条数
offset: 0 // 跳过条数
});
console.log(result);
2.3.7 查询数据(关联查询)
const result = UserModel.findComplex({
where: { age: { $gt: 18 } }, // 查询年龄大于18的用户
include: [ // 关联查询
{
model: HouseModel, // 关联的模型(必填)
as: 'houses', // 关联表的别名(必填)
columns: ['id', 'label'], // 关联表查询字段(可选)
where: { label: { $like: '%别墅%' } } // 关联条件(可选)
targetKey: 'id', // 外键关联字段 user表的id字段(必填)
foreignKey: 'ownerid', // 外键 house表的ownerid字段(必填)
// 【这里故意写错,上面定义模型字段为ownerId,但是SQLite对表名、字段名的大小写不敏感,所以能正常使用】
required: true, // true=INNER JOIN,false=LEFT JOIN(可选)
}
]
});
console.log(result);
2.3.8 查询数据(桥接关联表查询)
const result = UserModel.findComplex({
where: { age: { $gt: 18 } }, // 查询年龄大于18的用户
include: [ // 关联查询
{
model: HouseModel, // 关联的模型
as: 'houses', // 关联表的别名
through: {
table: 'relUserHouse', // 桥接表名
as: 'rel', // 桥接表别名
columns: ['id'], // 桥接表查询字段
mainKey: 'id', // 当前表的主键字段
mainForeignKey: 'ownerId', // 当前表的外键字段
otherKey: 'id', // 关联表的主键字段
otherForeignKey: 'houseId' // 关联表的外键字段
},
}
]
});
console.log(result);
三、核心API详解
3.1 SQLiteHelper 核心类
SQLiteHelper是插件的核心入口类,负责数据库连接管理、基础操作执行、模型定义等,所有数据库操作均基于此类实例。Model是基于操作表层面的方法类,所有表操作均基于Model实例。该类未暴漏给用户。如上的UserModel其实就是Model的实例。
3.1.1 构造函数
| 方法签名 | 说明 | 入参规范 | 注意事项 |
|---|---|---|---|
constructor(name: string) |
初始化数据库实例,自动创建/连接数据库 | name:数据库名称(仅字母/数字,以字母开头,不能为空) |
1. 名称会自动拼接.db后缀;2. 名称不合法会抛出异常; 3. 内部自动获取应用上下文,无需手动传入 |
3.1.2 连接管理
| 方法名 | 方法签名 | 作用 | 应用场景 | 返回值 | 注意事项 | 平台适配 |
|---|---|---|---|---|---|---|
| isOpen | isOpen(): boolean |
检查数据库连接是否开启 | 执行操作前校验连接状态 | boolean:true=已连接,false=未连接 |
无 | 安卓:✅️ 鸿蒙:✅️ IOS:❌️ |
| open | 安卓:open(): ExecuteResult;鸿蒙:open(): Promise<ExecuteResult> |
开启/重建数据库连接 | 连接异常时重新连接 | ExecuteResult:包含success、msg、data |
已连接时返回“无需开启”提示 | 安卓:✅️ 鸿蒙:✅️ IOS:❌️ |
| close | close(): ExecuteResult |
关闭数据库连接 | 页面销毁/应用退出时释放资源 | ExecuteResult:包含success、msg、data |
未连接时返回“无需关闭”提示 | 安卓:✅️ 鸿蒙:✅️ IOS:❌️ |
3.1.3 数据库信息查询
| 方法名 | 方法签名 | 作用 | 应用场景 | 返回值 | 注意事项 | 平台适配 |
|---|---|---|---|---|---|---|
| getVersion | getVersion(): string |
获取SQLite版本号 | 调试/兼容性校验 | string:版本号(如3.39.4) |
未连接时抛出异常;鸿蒙平台版本号固定为3.34.0 | 安卓:✅️ 鸿蒙:✅️ IOS:❌️ |
| isTableExists | isTableExists(tableName: string): ExecuteResult |
检查表是否存在 | 表操作前校验 | ExecuteResult:data包含exists(是否存在)、count(表数量) |
表名不能为空 | 安卓:✅️ 鸿蒙:✅️ IOS:❌️ |
| getAllTableNames | getAllTableNames(): ExecuteResult |
查询所有用户自定义表名 | 数据库结构校验 | ExecuteResult:data包含tableNames(表名数组)、count(表数量) |
排除sqlite_开头的系统表 | 安卓:✅️ 鸿蒙:✅️ IOS:❌️ |
| getTableInfo | getTableInfo(tableName: string): ExecuteResult |
查询表的完整结构 | 调试/表结构校验 | ExecuteResult:data包含字段信息、创建SQL |
表不存在时返回失败结果 | 安卓:✅️ 鸿蒙:✅️ IOS:❌️ |
| getPath | getPath(): string |
获取数据库文件所在位置 | 调试 | String |
表不存在时返回失败结果 | 安卓:✅️ 鸿蒙:❌️ IOS:❌️ |
| exportDbFile | exportDbFile(): void |
将数据库文件导出到公共目录Download下 | 调试/备份 | - | - | 安卓:✅️ 鸿蒙:❌️ IOS:❌️ |
3.1.4 基础数据操作
| 方法名 | 方法签名 | 作用 | 应用场景 | 返回值 | 注意事项 | 平台适配 |
|---|---|---|---|---|---|---|
| insert | insert(table: string, values: UTSJSONObject): ExecuteResult |
插入单条数据 | 单条数据新增 | ExecuteResult:data包含rowId(新增行ID) |
1. 表名/数据不能为空; 2. 自动转换数据类型(布尔值转0/1、时间转字符串) |
安卓:✅️ 鸿蒙:✅️ IOS:❌️ |
| insertBatch | insertBatch(tableName: string, values: UTSJSONObject[]): ExecuteResult |
批量插入数据 | 多条数据批量新增 | ExecuteResult:data=insertIds(行ID数组) |
1. 事务保障原子性; 2. 空数据会跳过并记录失败信息 |
安卓:✅️ 鸿蒙:✅️ IOS:❌️ |
| insertBatchWithTransaction | insertBatchWithTransaction(tableName: string, values: UTSJSONObject[]): ExecuteResult |
批量插入数据 | 多条数据批量新增 | ExecuteResult:data包含successCount(成功条数)、insertIds(行ID数组) |
1. 事务保障原子性; 2. 空数据会跳过并记录失败信息 |
安卓:✅️ 鸿蒙:✅️ IOS:❌️ |
| delete | delete(tableName: string, whereClause: string, whereArgs: string[]): ExecuteResult |
删除数据 | 条件删除数据 | ExecuteResult:data包含deleteCount(删除行数) |
1. whereClause不能为空; 2. 不支持直接删除表(删除表用runSql) |
安卓:❌️鸿蒙:✅️ IOS:❌️ |
| update | update(tableName: string, values: UTSJSONObject, whereClause: string, whereArgs: string[]): ExecuteResult |
更新数据 | 条件更新数据 | ExecuteResult:data包含updateCount(更新行数) |
1. 数据/条件不能为空; 2. 自动转换数据类型 |
安卓:✅️ 鸿蒙:✅️ IOS:❌️ |
| query | query(tableName: string, columns?: string[], whereClause?: string, whereArgs?: string[], orderBy?: string, limit?: string): ExecuteResult |
通用查询 | 安卓:单表基础查询 鸿蒙: 兼容所有查询 |
ExecuteResult:data包含count(总条数)、row(数据数组) |
支持分页、排序、条件过滤 | 安卓:✅️ 鸿蒙:✅️ IOS:❌️ |
| queryComplex | findComplex(sql: string, args?: string[], countSql?: string, countArgs?: string[]): ExecuteResult |
复杂查询 | 多表JOIN/子查询 | ExecuteResult:data包含count(总条数)、row(数据数组) |
支持任意复杂SQL,自动解析多表字段为嵌套对象 | 安卓:✅️ 鸿蒙:❌️ IOS:❌️ |
| runSql | runSql(sqlStr: string): ExecuteResult |
执行自定义SQL | 建表/删表/复杂SQL操作 | ExecuteResult:执行状态+消息 |
1. SQL不能为空; 2. 需自行保证SQL合法性 |
安卓:✅️ 鸿蒙:✅️ IOS:❌️ |
3.1.5 事务操作
| 方法名 | 方法签名 | 作用 | 应用场景 | 返回值 | 注意事项 | 平台适配 |
|---|---|---|---|---|---|---|
| beginTransaction | beginTransaction(): ExecuteResult |
开启事务 | 批量操作前开启 | ExecuteResult:执行状态+消息 |
未连接时返回失败 | 安卓:✅️ 鸿蒙:✅️ IOS:❌️ |
| submitTransaction | submitTransaction(): ExecuteResult |
标记事务成功 | 批量操作成功后调用 | ExecuteResult:执行状态+消息 |
提交前必须调用,否则事务回滚 | 安卓:✅️ 鸿蒙:✅️ IOS:❌️ |
| endTransaction | endTransaction(): ExecuteResult |
结束事务 | 批量操作最后调用 | ExecuteResult:执行状态+消息 |
无论成功失败都需调用,成功则提交,失败则回滚 | 安卓:✅️ 鸿蒙:❌️ IOS:❌️ |
| rollBack | rollBack(): ExecuteResult |
事务回滚 | 批量操作异常后回滚 | ExecuteResult:执行状态+消息 |
只有在异常失败时才能调用 | 安卓:❌️鸿蒙:✅️ IOS:❌️ |
3.1.6 模型定义
| 方法名 | 方法签名 | 作用 | 应用场景 | 返回值 | 注意事项 | 平台适配 |
|---|---|---|---|---|---|---|
| defineModel | defineModel(model: ModelDefType): Model |
定义模型并挂载CRUD方法 | 模型化开发 | Model:挂载了CRUD方法的模型实例 |
1. 自动创建表; 2. 表创建失败会抛出异常 |
安卓:✅️ 鸿蒙:✅️ IOS:❌️ |
3.2 Model 模型类
Model 类是基于模型定义生成的实例类,提供了ORM式的开发体验.封装了针对具体表的CRUD方法,无需关注底层SQL,直接通过对象方法操作数据。
- 优点: 是开发效率高,便于维护,对于SQL语言能力薄弱,熟练开发JS的开发人员友好,不用写繁琐的SQL语句.
- 缺点: 内部逻辑里增加了从数据模型->SQL语句的解析构建过程,转换过程有额外开销.某些复杂SQL难以用ORM表达.
3.2.1 核心方法
| 方法名 | 方法签名 | 作用 | 应用场景 | 入参规范 | 注意事项 | 平台适配 |
|---|---|---|---|---|---|---|
| insert | insert(data: UTSJSONObject): ExecuteResult |
插入单条数据 | 单条数据新增 | data:符合模型字段的对象 |
自动过滤非法字段,处理特殊类型(布尔值/时间) | 安卓:✅️ 鸿蒙:✅️ IOS:❌️ |
| insertBatch | insertBatch(dataList: UTSJSONObject[]): ExecuteResult |
批量插入数据 | 多条数据新增 | dataList:符合模型字段的对象数组 |
事务保障原子性 | 安卓:✅️ 鸿蒙:✅️ IOS:❌️ |
| insertBatchWithTransaction | insertBatchWithTransaction(dataList: UTSJSONObject[]): ExecuteResult |
批量插入数据 | 多条数据新增 | dataList:符合模型字段的对象数组 |
事务保障原子性 | 安卓:❌️鸿蒙:✅️ IOS:❌️ |
| update | update(data: UTSJSONObject, options: UpdateOptions): ExecuteResult |
更新数据 | 条件更新 | data:更新数据;options:包含where条件(必填) |
where条件不能为空 | 安卓:✅️ 鸿蒙:✅️ IOS:❌️ |
| delete | delete(options: DeleteOptions): ExecuteResult |
删除数据 | 条件删除 | options:包含where条件(必填) |
where条件不能为空 | 安卓:✅️ 鸿蒙:✅️ IOS:❌️ |
| find | find(options: QueryOptions): ExecuteResult |
基础查询 | 单表查询 | options:包含columns/where/limit/offset/order |
自动转换where条件为SQL | 安卓:✅️ 鸿蒙:✅️ IOS:❌️ |
| findComplex | findComplex(options: ComplexQueryOptions): ExecuteResult |
复杂查询 | 多表关联查询 | options:包含columns/where/include/limit/offset/order |
支持一对一/一对多/多对多关联 | 安卓:✅️ 鸿蒙:❌️ IOS:❌️ |
- ⚠️ 注意: 鸿蒙平台的查询方法只有 find 一个封装方法,使用效果和安卓端findComplex相同. 安卓端 find和findComplex的区别在于: find适合用来查询单表数据,内部开销小.findComplex 可用来构建复杂的语句(如关联查询),内部开销大.
四、类型定义规范
4.1 核心类型说明
核心类型如下:
4.1.1 ExecuteResult 执行结果返回类型
/** 插件执行返回结果类型 */
export type ExecuteResult = {
success : boolean; // 执行是否成功
msg ?: string; // 执行消息(失败时返回错误信息)
data ?: UTSJSONObject | string | boolean | number | Array<any> | PageResult | null; // 结果数据
}
/** 分页查询结果类型 */
export type PageResult = {
count : number; // 总记录数
row : UTSJSONObject[]; // 数据行数组
}
- 数据示例
// 成功示例
{
success: true,
msg: '操作成功',
data: {
count: 3,
row: [
{id: 1, name: '张三', age: 20, createAt: '2022-01-01 12:00:00'},
{id: 2, name: '李四', age: 20, createAt: '2022-01-01 12:00:00'},
{id: 3, name: '王五', age: 20, createAt: '2022-01-01 12:00:00'}
]
}
}
// 失败示例
{
success: false,
msg: 'SQL执行失败:原因:',
data: null
}
4.1.2 ModelDefType 模型定义类型
/** 模型定义类型 */
export type ModelDefType = {
tableName : string; // 表名
columns : tableColumnType[]; // 字段列表
foreignKeys ?: ForeignKeyConfig[]; // 外键配置
uniqueKeys ?: UniqueConstraint[]; // 复合唯一约束(如 UNIQUE(ownerId, houseId))
};
/** 模型中的表字段类型 */
export type tableColumnType = {
name : string; // 字段名
type : "INTEGER" | "TEXT" | "REAL" | "BLOB" | "BOOLEAN" | "DATE"; // 字段类型
primaryKey ?: boolean; // 是否主键
autoIncrement ?: boolean; // 是否自增
notNull ?: boolean; // 是否允许为空
default ?: any; // 默认值
unique ?: boolean; // 单字段是否唯一(如 unique: true 等价于 UNIQUE(字段名))
};
/** 外键定义类型 */
export type ForeignKeyConfig = {
field : string; // 当前表的外键字段(如house.ownerId)
refTable : string; // 关联的主表名(如user)
refField : string; // 主表的关联字段(如user.id)
onDelete ?: string; // 级联操作(如CASCADE/SET NULL)
onUpdate ?: string; // 级联更新(如CASCADE)
}
/** 复合唯一约束类型 */
export type UniqueConstraint = {
name ?: string; // 约束名称(可选,SQLite会自动生成)
fields : string[]; // 唯一约束的字段列表(单字段:['ownerId'],复合:['ownerId', 'houseId'])
};
- 数据示例: 参看 2.2 基础使用流程 -> 2.2.1 数据库初始化与模型导出
4.2 字段类型映射
插件支持的字段类型与SQLite原生类型的映射关系:
| 插件类型 | SQLite原生类型 | 说明 |
|---|---|---|
| INTEGER | INTEGER | 整数类型,支持自增/主键 |
| TEXT | TEXT | 字符串类型,DATE类型也存储为TEXT |
| REAL | REAL | 浮点数类型 |
| BLOB | BLOB | 二进制数据类型 |
| BOOLEAN | INTEGER | 存储为0(false)/1(true) |
| DATE | TEXT | 存储为YYYY-MM-DD HH:mm:ss格式字符串,默认值NOW对应本地时间 |
五、最佳实践
5.1 目录结构规范
uni_modules/
└── se-sqlite/ # 插件核心目录
utils/
└── db/ # 数据库管理目录
├── models/ # 模型定义目录
│ ├── user.ts # 用户表模型
│ ├── house.ts # 房产表模型
│ └── relUserHouse.ts # 多对多关联表模型
└── index.ts # 数据库初始化与模型导出
pages/
└── index/
└── index.uvue # 业务页面使用模型
5.2 模型定义规范
- 表名/字段名仅包含字母/数字,且以字母开头;
- 主键建议命名为id,类型为INTEGER且开启autoIncrement;
- 一张表中建议(必须)设置一个主键,如果没有主键在复杂查询整合数据时会失败;
- 时间类型的字段建议将类型设为为DATE(虽然SQLite内部没有Date类型的数据),插件会自动帮你转换成TUS时间字符串。如果同时设置默认值设为 'NOW',在你新增呢个数据且美没有传该字段值时,数据库也会为你自动生成TUS时间字符串(相当于SQL: CREATE_TIME` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP);
- 多对多关联表建议命名为rel+主表名+关联表名(如relUserHouse)。
5.3 where 条件规范
// ------------- 正确用法示例 -------------//
SomeModel.find({
where: {
a: 'hello', // a = 'hello'
$or: [{ a: 5 }, { b: { $eq: 6 }}, {c: {$in: [1, 2, 3]}} }], // (a = 5) OR (b = 6) OR (c IN [1, 2, 3])
$and: [{a:{$gt: 5}},{a: {$ne: 10}}], // a > 5 AND a !=10 //1.0.5 新增
// $or 与 $and 嵌套使用
$or:[{a:5},{$and:[{b: {$gt: 10}}, {c:{$ne: 9}}]}], // (a = 5) OR (b > 10 AND c != 9)
$and: [{a:{$ne: 5}},{$or:[{b: {$gt: 1}}, {b:{$lt: 9}}]}], // a != 5 AND (b > 1 AND b < 9)
a: {$gt: 5, $ne: 10}, // a > 5 AND a !=10
d: {
// 基本
$eq: 3, // = 3
$ne: 20, // != 20
$is: null, // IS NULL
$not: true, // IS NOT TRUE
// 数字比较
$gt: 6, // > 6
$gte: 6, // >= 6
$lt: 10, // < 10
$lte: 10, // <= 10
$between: [6, 10], // BETWEEN 6 AND 10
$notBetween: [11, 15], // NOT BETWEEN 11 AND 15
// 其它操作符
$in: [1, 2], // IN [1, 2]
$notIn: [1, 2], // NOT IN [1, 2]
$like: '%hat', // LIKE '%hat'
$notLike: '%hat', // NOT LIKE '%hat'
$like: ['%hat%','%cat'], // LIKE '%hat' OR LIKE '%cat'
$notLike: ['%hat','%cat'], // NOT LIKE '%hat' AND NOT LIKE '%cat'
}
}
});
// ------------- 错误用法示例 -------------//
SomeModel.find({
where: {
$or: [
{ $or: [
{a:5},
{c:{$gt:8}}
]
},
{ b: 6 }
],
// $or 只能出现在where对象的第一层,其值只能是[条件对象]数组.[条件对象]只能是 {a: 5} 或 {c: {$gt: 8}这种格式
$eq: 'hello', // where对象的第一层只能是$or和表的列名,不能是其它操作符
d: {a: 5}. // d代表的是列名,那么a处只能是操作符,所以这个是错误的
e: {$between: [9,100, 300]} // $between 只能接受两个参数,所以这个是错误的
}
});
// Model 示例的方法中会将上面的where对象解析成SQL中的where条件,由于作者能力有限,只能解析出上面这种格式的where对象,所以请尽量按照上面的正确格式来写where条件
// 如果示例中的where条件不能满足你的需求,既可以参看下方的进阶教程,自己写原生SQL.调用底层API来执行复杂的查询
5.4 事务使用示例
// 批量插入数据,事务保障
// ------------------------------- 安卓端示例 ------------------------------- //
try {
sqlite.beginTransaction(); // 开启事务
const result1 = UserModel.insert({ name: '张三', age: 20 });
const result2 = HouseModel.insert({ label: '北京某小区', ownerId: result1.data.rowId });
sqlite.submitTransaction(); // 标记事务成功
sqlite.endTransaction(); // 结束事务(提交)
console.log('批量插入成功');
} catch (e) {
sqlite.endTransaction(); // 结束事务(回滚)
console.error('批量插入失败,事务回滚:', e);
}
// 或者
try {
sqlite.beginTransaction(); // 开启事务
const result1 = UserModel.insert({ name: '张三', age: 20 });
const result2 = HouseModel.insert({ label: '北京某小区', ownerId: result1.data.rowId });
sqlite.submitTransaction(); // 标记事务成功
console.log('批量插入成功');
} catch (e) {
console.error('批量插入失败,事务回滚:', e);
} final {
// 无论成功与否,最终都需要调用endTransaction结束事务,否则会阻塞后续操作
// 所以只要是开启了事务,一定要保证后续代码能够执行到endTransaction
sqlite.endTransaction(); // 结束事务(回滚)
}
// ------------------------------- 鸿蒙端示例 ------------------------------- //
let t: ExecuteResult | null = null
try {
const openResult = sqlite.beginTransaction(); // 开启事务
t= openResult
if(!openResult.success) {
throw new Error('事务开启失败');
}
const result1 = UserModel.insert({ name: '张三', age: 20 });
const result2 = HouseModel.insert({ label: '北京某小区', ownerId: result1.data.rowId });
const submitResult = sqlite.submitTransaction(); // 标记事务成功
if(!submitResult.success) {
throw new Error('事务提交失败');
}
} catch (e) {
if(t.success) {
sqlite.rollBack(); // 回滚事务
}
}
// --------------- ⚠️ 注意以上 安卓端 和 鸿蒙端 对于事务的处理方式的不同 ⚠️ --------------- //
5.5 复杂查询时的注意事项
// 示例1. A->B 关联查询
const result = UserModel.queryComplex({
columns: ['name', 'age'], // 指定查询字段
where: { name: '张三' }, // 查询条件
include: [
{
model: HouseModel, // 关联模型
required: false, // 是否必须关联,为true时无关联数据会被过滤只返回有关联的数据.比如张三名下没房产,则不会返回张三的数据
as: 'house', // 关联表别名 ⚠️ 此处别名的命名注意不要和 UserModel 中的字段名冲突()
foreignKey: 'ownerId', // 外键字段 ⚠️ 必须指定
targetKey: 'id', // 关联表主键 ⚠️ 必须指定
},
],
});
// 示例2. A<- B ->C 通过中间表关联查询
const result2 = UserModel.findComplex({
columns: ['name', 'age'], // 指定查询字段
include: [{
model: HouseModel.modelDef,
as: 'houses',
columns: ['id', 'label'],
required: true,
through: {
table: 'relUserHouse', // 中间表名称
as: 'rel', // 中间表别名 (非必填)
columns: ['id'], // 中间表字段 (非必填)
mainKey: 'id', //⚠️ 必须指定
mainForeignKey: 'ownerId', //⚠️ 必须指定
otherKey: 'id', //⚠️ 必须指定
otherForeignKey: 'houseId' //⚠️ 必须指定
// 通过中间表关联查询时,中间表必须存在,否则会抛出异常
// 一般情况下,中间表作为桥接作用,as 和 columns 可以不指定,因为中间表一般只存储两个表的外键.除非有业务场景需要确定 user和house的关联时间时才需要指定 as 和 columns
},
}],
limit: 10,
offset: 20
})
5.5 批量插入时的注意事项
- insertBatch 在本插件中都是经过封装后的方法,但内部调用的底层逻辑不同.
- 安卓端: insertBatch 内部启用了事务,通过遍历数据->循环插入单条来实现的,所以批量插入时,如果中途抛出异常,事务会回滚,插入的数据会全部失效. ⚠️ 因内部启用了事务,所以不要再其它事务中调用 insertBatch以免造成事务嵌套
- 鸿蒙端: insertBatch 调用的是鸿蒙原生的RelationalStore.RdbStore.batchInsertSync方法.非循环操作,所以内部无事务. ⚠️但这里有个注意点是batchInsertSync底层执行的是INSERT OR IGNORE 语法.所以存在一种情况就是如上方relUserHouse表,虽然有Unique约束,但插入的数据中如果存在重复数据,鸿蒙端会忽略这些重复数据,不会抛出异常.而且如果该表的主键支持自增,在你反复插入同一条数据时,该条数据的id会不断自增,但表中数据的总数不变.
- 考虑到3中的情况,插件中在鸿蒙端独有一个insertBatchWithTransaction方法,内部逻辑和安卓端insertBatch一样,开启事务+循环插入单条.所以如果需要保证数据一致性,请使用insertBatchWithTransaction方法.
5.6 groupBy 和 having 示例
- 示例一 单表聚合查询
const result = UserModel.findComplex({
columns: [
'id',
'name',
{ func: 'COUNT', field: 'id', as: 'counts'},
{ func: 'SUM', field: 'amount', as: 'totalAmount'},
{ func: 'MAX', field: 'amount', as: 'maxAmount'},
],
where: { id: { $gt: 1 } },
groupBy: ['id', 'name'], // 按照SQLite的兼容性,上面colums里有几个表原始字段,这里就要写几个字段(豆包说的,我也不懂)
having: { counts: { $gte: 2 }, totalAmount: { $gte: 500 } },
order: [['totalAmount', 'DESC']],
offset: 0,
limit: 10
})
console.log(result)
--示例一得到的SQL语句:
SELECT
main.id,
main.name,
COUNT(main.id2) AS counts,
SUM(main.amount) AS totalAmount,
MAX(main.amount) AS maxAmount
FROM user main
WHERE (main.id > ?)
GROUP BY
main.id,
main.name
HAVING (
counts >= ?
AND totalAmount >= ?
)
ORDER BY totalAmount DESC
LIMIT 10
OFFSET 0;
- 示例二 联合查询聚合
const result = UserModel.findComplex({
columns: [
'id',
'name',
{ func: 'COUNT', field: 'id2', as: 'counts', targetTable: 'bills' },
{ func: 'SUM', field: 'amount', as: 'totalAmount', targetTable: 'bills' },
{ func: 'MAX', field: 'amount', as: 'maxAmount', targetTable: 'bills' },
],
where: { id: { $gt: 1 } },
include: [
{
model: BillModel.modelDef,
as: 'bills',
columns: [],
targetKey: 'id',
foreignKey: 'ownerId',
required: false,
order: [['id', 'DESC']],
where: { status: 1, createAt: { $gte: '2025-01-01', $lt: '2026-01-01' } }
}
],
groupBy: ['id', 'name'],
having: { counts: { $gte: 2 }, totalAmount: { $gte: 500 } },
order: [['totalAmount', 'DESC']],
offset: 0,
limit: 10
})
console.log(result)
-- 示例二 得到的SQL语句
ELECT
main.id,
main.name,
COUNT(bill.id2) AS counts,
SUM(bill.amount) AS totalAmount,
MAX(bill.amount) AS maxAmount,
bill.id AS bill_id
FROM user main
LEFT JOIN bill bill ON main.id = bill.ownerId
AND bill.id IN (
SELECT bill.id
FROM bill bill
WHERE
bill.ownerId = main.id
AND (
bill.createAt >= ?
AND bill.createAt < ?
AND bill.status = ?
)
)
WHERE (main.id > ?)
GROUP BY
main.id,
main.name
HAVING (
counts >= ?
AND totalAmount >= ?
)
ORDER BY totalAmount DESC
LIMIT 10
OFFSET
0;
- 注意事项:
- ⚠️ 目前聚合函数仅支持: 'COUNT' | 'SUM' | 'MAX' | 'MIN' | 'AVG';
- ⚠️ 示例二 中的 targetTable 字段要与当前字段所属表的别名相同. 比如:
{ func: 'SUM', field: 'amount', as: 'totalAmount', targetTable: 'bills' },,amount字段其实是bill表中的字段,而下方include语句中bill表将别名改成了bills(as: 'bills'). 所以targetTable这个字段的值要与as的值相同.
六、注意事项
6.1 通用注意事项
- 创建表的过程建议使用
defineModel方法,避免手动编写SQL,提高开发效率; - 数据库名称/表名/字段名仅支持字母/数字,且必须以字母开头,否则会抛出异常;
- 所有操作均需保证数据库连接已开启(isOpen返回true),否则会返回失败结果;
- 批量操作建议使用事务,避免部分成功部分失败;
- 查询结果的游标会自动关闭,无需手动处理,但复杂查询需注意结果集大小,避免内存溢出;
- 外键约束需在模型定义中配置,SQLite默认开启外键支持,插件已适配。
6.2 性能注意事项
- 大量数据查询建议使用分页(limit/offset),避免一次性查询全部数据;
- 多表关联查询尽量指定columns,只查询需要的字段,减少数据传输;
- 频繁的插入/更新操作建议使用批量方法(insertBatch),减少数据库IO;
6.3 兼容性注意事项
- 该插件目前支持Android端/鸿蒙端,基于UniApp X的UTS开发,不支持iOS/小程序;
- SQLite版本由设备系统决定,插件已兼容主流版本(3.x)。
七、进阶教程
- 由于插件
ORM模式开发的局限性(无法构建复杂的SQL语句,并保证SQL性能),所以为保证能够适配复杂业务场景,本插件提供了底层API来执行复杂的查询.允许用户自己构建sql语句来执行.对比ORM开发模式, 直接调用底层API基本无额外消耗,且灵活度更高,适配场景更广,但对于开发者的SQL语言的水平要求也更高.
7.1 安卓端进阶教程
7.1.1 runSql(sql: string) : ExecuteResult
- 底层API
execSQL(String sql) - 作用:执行无返回结果的原生 SQL 语句;
- 支持操作:建表、删表、ALTER 语句、INSERT/UPDATE/DELETE(无返回值)、创建索引等;
- 使用场景:执行非查询类原生 SQL,无需获取执行结果;
- 示例:
// 执行建表SQL sqlite.runSql("CREATE TABLE IF NOT EXISTS house (id INTEGER PRIMARY KEY, ownerId INTEGER)"); // 执行插入SQL sqlite.runSqlL("INSERT INTO house (ownerId) VALUES (1)"); // 执行删除SQL sqlite.runSql("DELETE FROM house WHERE ownerId = 1"); // 返回结果: { success: true, // true: 标识SQL语句执行成功 false: 标识SQL语句执行失败 msg: '执行成功', // 当实行失败时,这里展示报错信息 dataL: null }7.1.2
rawQuery(sql : string, args : string[]) : ExecuteResult - 底层API
rawQuery(String sql, String[] selectionArgs) - 作用:执行有返回结果的原生 SQL 语句(仅 SELECT);
- 参数:
sql- 原生查询 SQL(可带占位符?),selectionArgs- 占位符对应的参数数组(按顺序匹配,避免SQL注入); - 返回值:
Cursor- 查询结果集,需遍历处理,使用后必须关闭; - 使用场景:复杂查询(多表关联、GROUP BY、HAVING、子查询等),封装方法无法实现时;
- 示例:
const sql = `SELECT * FROM user WHERE age > ? AND name LIKE ?`
const args = ["20", "%张%"]
const result = sqlite.rawQuery(sql,args);
// 该方法内部对返回结果只进行了简单的便利转化,所以复杂的联合查询结果是扁平化的json数据,需要用户自己处理数据结构
console.log(result)
// 返回结果:
{
success: true, // true: 标识SQL语句执行成功 false: 标识SQL语句执行失败
msg: '执行成功', // 当实行失败时,这里展示报错信息
dataL: [
...
]
}
7.2 鸿蒙端进阶教程
7.2.1 runSql(sql: string) : ExecuteResult
- 底层API
executeSync(sql: string, args?: Array<ValueType>): ValueType - 作用:执行无返回结果的原生 SQL 语句;
- 支持操作:建表、删表、ALTER 语句、INSERT/UPDATE/DELETE(无返回值)、创建索引等;
- 使用场景:执行非查询类原生 SQL,无需获取执行结果;
- 示例:同安卓
7.2.2 querySqlSync(sql : string, args : Array<string | number> | null) : ExecuteResult
- 底层API
querySqlSync(sql: string, bindArgs?: Array<ValueType>):ResultSet - 作用:根据指定SQL语句查询数据库中的数据;
- 支持操作:SELECT 语句;
- 使用场景:执行大部分SELECT查询语句;
- 示例:
const sql = `SELECT * FROM user WHERE age > ? AND name LIKE ?`
const args = ["20", "%张%"]
const result = sqlite.querySqlSync(sql,args);
// 该方法内部对返回结果只进行了简单的便利转化,所以复杂的联合查询结果是扁平化的json数据,需要用户自己处理数据结构
console.log(result)
// 返回结果:
{
success: true, // true: 标识SQL语句执行成功 false: 标识SQL语句执行失败
msg: '执行成功', // 当实行失败时,这里展示报错信息
dataL: [
...
]
}
8 特别感谢
- 感谢
豆包在开发过程中的大力支持,和在本文档书写过程中的贡献;

收藏人数:
购买源码授权版(
试用
使用 HBuilderX 导入示例项目
赞赏(0)
下载 29
赞赏 0
下载 11308989
赞赏 1869
赞赏
京公网安备:11010802035340号