RdbPlus
哈啰出行SQLite的ORM框架,无需编写sql代码,通过装饰器解析表结构,一行搞定增删改查
Install / Use
/learn @yongoe1024/RdbPlusREADME
rdb-plus 使用文档
简介
SQLite的ORM框架,无需编写sql代码,通过装饰器解析表结构,一行搞定增删改查
使用API5.0.0(12)编译,已在哈啰出行、一汽奥迪等APP中使用
QQ交流群:1056151906
版本说明(使用方法与文档有较大变化)
v3.1.2
- 新增Wrapper支持传入条件,控制是否应用这条语句。例:eq('name', '张三', 判断条件)
- 增删改操作现在具有返回值,详情可查看函数说明
v3.1.1
- 优化了初始化方法与失败提示
- 更新REAMDE中案例,所有操作都用try-catch捕获异常信息
- 增加了版本管理version、备份数据库、还原数据库、删除数据库、获取原生store等函数
- 查询结果为实体类的实例
- @TableField可指定表的字段名。如果不设置,将使用实体类中的字段名作为数据库表的主键字段名。
下载安装
- 安装最新版
ohpm i rdbplus - 升级版本
ohpm update rdbplus,建议使用最新版避免bug
OpenHarmony ohpm 环境配置等更多内容,请参考如何安装 OpenHarmony ohpm 包
使用案例
https://gitee.com/yongoe/RdbPlus/tree/main/entry/src/main/ets
https://github.com/yongoe1024/RdbPlus/tree/main/entry/src/main/ets
功能介绍
| 函数名 | 介绍 | |----------------|-----------------------| | count | 查询符合条件的记录总数 | | getList | 查询符合条件的记录 | | getPage | 分页查询符合条件的记录 | | getOne | 查询第一个符合条件的记录 | | getObject | 查询符合条件的记录,返回对象数组 | | getObjectBySql | 输入SQL查询符合条件的记录,返回对象数组 | | getById | 根据主键查询 | | insert | 插入一条记录 | | insertBatch | 插入多条记录 | | updateById | 根据主键更新数据 | | update | 更新符合条件的记录 | | deleteById | 根据主键删除 | | delete | 删除符合条件的记录 |
引入教程
- 首先引入ohpm依赖:
ohpm i rdbplus - 在
EntryAbility的onCreate中调用Connection.init()进行初始化 - 创建一个数据库表对应的ets类,并用装饰器
@Table、@TableField标注 - 调用BaseMapper.build()构造实例
- 在页面调用实例,使用增删改查的方法,无需编写SQL代码
初始化
在EntryAbility调用 Connection.init() 进行初始化
传入两个参数:Context、日志DbLogger(可空),不需要日志可以不传
注:rdbplus提供了DbLogger,可以自行继承Logger接口构造日志对象
import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';
import { window } from '@kit.ArkUI';
import { Connection, DbLogger } from 'rdbplus';
export default class EntryAbility extends UIAbility {
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
Connection.init(this.context, new DbLogger())
}
onWindowStageCreate(windowStage: window.WindowStage) {
windowStage.loadContent('pages/Index', (err) => {
});
}
}
第一步:创建实体类
- 创建
Employee.ets,字段名要与数据库字段一一对应 - 数据库没有的字段,不需要标注
@TableField, - 当数据库表中有字段,而class没有,不影响结果,数据库返回的结果,都可以正常从返回结果中取出来
- 数据表字段类型与class字段类型不一致,不影响结果,ets的类型仅在编译期限制(建议类型保持一致)
import { Table, TableField, FieldType } from "rdbplus"
@Table({ tableName: 't_emp' })
export class Employee {
/**
* id,数据库字段为id
*/
@TableField({ name: 'id', type: FieldType.NUMBER, isPrimaryKey: true })
id?: number
/**
* 名字,数据库字段为name,同名可省略TableField.name
*/
@TableField({ type: FieldType.TEXT, })
name?: string
/**
* 年龄,数据库字段为age,myAge与数据库字段age互相映射
*/
@TableField({ name: 'age', type: FieldType.NUMBER, })
myAge?: number
/**
* 其他字段,会被sql忽略,不参与操作
*/
other?: number
}
第二步:创建Mapper类
- 创建static变量
private static mapper: BaseMapper<Employee> - 使用
BaseMapper.build()创建实例,参数分别为实体类,数据库配置
import { relationalStore } from '@kit.ArkData'
import { BaseMapper } from 'rdbplus'
import { Employee } from './Employee'
export class EmpMapper {
private static mapper: BaseMapper<Employee>
// 单例模式
static getInstance() {
if (!EmpMapper.mapper) {
EmpMapper.mapper = BaseMapper.build<Employee>({
class: Employee,
config: {
name: 'RdbTest.db', // 数据库文件名
securityLevel: relationalStore.SecurityLevel.S3, // 数据库安全级别
encrypt: false, // 可选参数,指定数据库是否加密,默认不加密
customDir: 'customDir/subCustomDir', // 可选参数,数据库自定义路径。
isReadOnly: false // 可选参数,是否以只读方式打开。默认为false
}
})
}
return EmpMapper.mapper
}
}
第三步:页面中调用
@Entry
@Component
struct Index {
mapper = EmpMapper.getInstance()
build() {
Column() {
Button('count').onClick(async () => {
try {
// 入参可空,即不构造条件查询
let num = await this.mapper.count(new Wrapper())
showDialog(num + '')
} catch (e) {
console.error(e)
}
})
}
}
}
创建表、数据库版本管理、复杂SQL
- 调用
EmpMapper对象中的getConnection()方法,得到一个Connection对象 execDML执行创建、添加、修改语句,execDQL执行查询语句
console.log('删除数据库')
Connection.deleteRdbStore('RdbTest.db')
/**
* 创建表
* 修改表结构需要控制版本
* 修改后数据库版本增加,且无法降低版本
*/
function createTable() {
let db: Connection | undefined = undefined
try {
db = await EmpMapper.getInstance().getConnection()
console.log('createTable 首次打开 版本 0');
// 初始默认 0
if (db.version === 0) {
await db.execDML(
`create table if not exists "t_emp" (
id integer primary key autoincrement,
name varchar(20)
)`, [])
db.version = 1
console.log('createTable 创建表后 版本 1')
}
if (db.version === 1) {
// 添加字段
db.execDML('ALTER TABLE t_emp ADD COLUMN age integer');
db.version = 2;
console.log('createTable 修改后 版本 2')
}
if (db.version === 2) {
// 以此类推
console.log('createTable 最终版本 2')
}
} catch (e) {
console.error(e)
} finally {
db?.close()
}
}
装饰器介绍
Table
用于指定实体类对应的数据库表名
| 入参 | 说明 | |-------------------|-------| | tableName: string | 数据库表名 |
TableField
用于标记实体类中的字段
| 入参 | 说明 | |------------------------|--------| | name?: string | 数据库字段名 | | type: FieldType | 字段类型 | | isPrimaryKey?: boolean | 是否主键 |
API介绍
count
查询符合条件的记录总数
| 入参 | 说明 | |-------------------|-------| | wrapper?: Wrapper | 条件构造器 |
| 返回值 | 说明 | |--------|-----| | number | 统计值 |
try {
let num = await this.mapper.count(new Wrapper())
} catch (e) {
console.error(e)
}
getObject
查询符合条件的记录,返回一个对象
| 入参 | 说明 | |-------------------|-------| | wrapper?: Wrapper | 条件构造器 |
| 返回值 | 说明 | |------------|------| | EsObject[] | 对象数组 |
try {
const res: ESObject = await this.mapper.getObject(new Wrapper().eq('name', '123'))
} catch (e) {
console.error(e)
}
getObjectBySql
传入SQL,查询符合条件的记录,返回对象数组
| 入参 | 说明 | |-------------------------------------|-------| | sql: string | sql语句 | | params: relationalStore.ValueType[] | 占位符参数 |
| 返回值 | 说明 | |------------|------| | EsObject[] | 对象数组 |
try {
const res: ESObject = await this.mapper.getObjectBySql('select count(*) from t_emp where age = ? ', [13])
} catch (e) {
console.error(e)
}
getList
查询符合条件的记录,返回对象数组
| 入参 | 说明 | |-------------------|-------| | wrapper?: Wrapper | 条件构造器 |
| 返回值 | 说明 | |-----|------| | T[] | 对象数组 |
try {
const res = await this.mapper.getList(new Wrapper().eq('name', '123'))
} catch (e) {
console.error(e)
}
getOne
查询符合条件的第一条数据
| 入参 | 说明 | |-------------------|-------| | wrapper?: Wrapper | 条件构造器 |
| 返回值 | 说明 | |-----|------| | T | 实体对象 |
try {
const res = await this.mapper.getOne(new Wrapper().eq('name', '123'))
} catch (e) {
console.error(e)
}
getPage
分页查询符合条件的记录,返回分页结果
| 入参 | 说明 | |-------------------|-------| | current: number | 查询页数 | | size: number | 每页数量 | | wrapper?: Wrapper | 条件构造器 |
| 返回值 | 说明 | |---------|--------| | Page<T> | 分页查询结果 |
| Page | 说明 | |---------|------| | total | 总数 | | current | 当前页 | | size | 每页数量 | | record | 结果集 |
try {
// 查询第一页的数据,返回Page对象
const page = await this.mapper.getPage(1, 10)
// 总数
const total = page.total
// 当前页
const current = page.current
// 每页条数
const size = page.size
// 结果集
const record = page.record
} catch (e) {
console.error(e)
}
getById
根据主键查询,返回一个实体对象
| 入参 | 说明 | |---------------|-----| | id: ValueType | 主键值 |
| 返回值 | 说明 | |---------------|-------------------------| | T 或 undefined | 存在返回实体对象,不存在返回undefined |
try {
const res = await this.mapper.getById(3)
} catch (e) {
