# RdbPlus **Repository Path**: yongoe/RdbPlus ## Basic Information - **Project Name**: RdbPlus - **Description**: 这是一个sqlite的增强工具,无需编写sql代码,一行搞定增删改查,为简化开发、提高效率而生。 - **Primary Language**: TypeScript - **License**: MIT - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 4 - **Forks**: 1 - **Created**: 2024-10-16 - **Last Updated**: 2025-04-21 ## Categories & Tags **Categories**: Uncategorized **Tags**: Sqlite, ArkTS, 鸿蒙 ## README # rdb-plus 使用文档 ## 简介 这是一个sqlite的增强工具,无需编写sql代码,通过继承BaseMapper类,一行搞定增删改查,为简化开发、提高效率而生。(类似Mybatis-plus) ## 版本说明 2.0.0 1. 修改命名 wapper->wrapper 2. 使用API 5.0.3(15) ## 下载安装 `ohpm i rdbplus` OpenHarmony ohpm 环境配置等更多内容,请参考[如何安装 OpenHarmony ohpm 包](https://ohpm.openharmony.cn/#/cn/help/downloadandinstall) ## 使用案例 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 | 分页查询符合条件的记录 | | getObject | 查询符合条件的记录,返回对象数组 | | getObjectBySql | 输入SQL查询符合条件的记录,返回对象数组 | | getById | 根据 ID 查询 | | insert | 插入一条记录 | | updateById | 根据 ID 更新数据 | | update | 更新符合条件的记录 | | deleteById | 根据 ID 删除 | | delete | 删除符合条件的记录 | ## 引入教程 1. 首先引入ohpm依赖:`ohpm i rdbplus` 2. 创建一个数据库表对应的实体类,推荐ts格式,比如`Employee.ts` 3. 创建一个mapper类,比如 `EmpMapper.ets` 4. 直接在页面中`new出EmpMapper`,就可以调用增删改查的方法,无需编写SQL代码 ### 第一步:创建实体类 推荐在`src/main/ets/entity`路径中,创建ts文件`Employee.ts`(可以不写默认值) !!!字段名要与数据库字段一一对应,完全一致!!! ```typescript export class Employee { id: number name: string constructor() { } } ``` ### 第二步:创建Mapper类 推荐在`src/main/ets/mapper`路径中,创建ets文件`EmpMapper.ets` 1. 首先创建`EmpMapper类`,然后继承`BaseMapper`,传入泛型`Employee` 2. 创建构造函数,调用super方法。 第一个参数是`一个对象`,包含`表名`、`主键字段名`两项内容 第二个参数是`回调函数`:`(res: relationalStore.ResultSet)=> T`,返回一个泛型对象,本意是为了从ResultSet中得到一行数据 第三个参数是`可选值`,传入`relationalStore.StoreConfig`,比如数据库名、安全级别等。默认库名`demo.db` ```typescript import { Employee } from '../entity/Employee' import { relationalStore } from '@kit.ArkData' import { BaseMapper } from 'rdbplus' const getRow = (res: relationalStore.ResultSet) => { const emp = new Employee() emp.id = res.getLong(res.getColumnIndex('id')) emp.name = res.getString(res.getColumnIndex('name')) return emp } export class EmpMapper extends BaseMapper { constructor() { super( { tableName: 't_emp', primaryKey: 'id' }, getRow, // 可选参数 { name: 'demo.db', securityLevel: relationalStore.SecurityLevel.S1 } ) } } ``` ### 第三步:页面中调用 ``` import { EmpMapper } from '../mapper/EmpMapper' @Entry @Component struct Index { empMapper = new EmpMapper() build() { Button('查询全部数据').onClick(() => { // 查询全部 let list:1 Employee[] = await this.empMapper.getList(new Wrapper()) // 条件查询 let list2: Employee[] = await this.empMapper.getList(new Wrapper().eq('name', '李四')) // 统计总数 const num:number = await this.empMapper.count(new Wrapper()) }) } } ``` ### 建表、连接查询等复杂SQl,采用手写SQL方法 1. 调用`EmpMapper`对象中的`getConnection()`方法,得到一个`Connection`对象 2. `Connection`有两个函数:`execDML`、`execDQL` `execDML`是数据操纵函数,执行创建、添加、修改 语句 `execDQL`是数据查询函数,执行查询语句 #### 示例 在`EmpMapper.ets`中,添加一个函数`createTable`,用来创建表 ```javascript export class EmpMapper extends BaseMapper { /** ...其余省略 ... */ async createTable() { const db = await this.getConnection() await db.execDML(`DROP TABLE IF EXISTS t_emp ;`) await db.execDML( `create table if not exists "t_emp" ( id integer primary key autoincrement, name varchar(20) )`) await db.execDML(`INSERT INTO t_emp (id,name) VALUES (null, ? );`, ['111']) await db.close() } } ``` ## API介绍 以下代码示例的前提条件是:已经实现了一个mapper类,例如`EmpMapper` ### count 查询符合条件的记录总数 返回值: 符合条件的记录总数。 | 入参 | 说明 | |----------------|------| | wrapper: Wrapper | 查询条件 | | 返回值 | 说明 | |--------|-----| | number | 统计值 | ```typescript // 统计总数 const num:number = await this.empMapper.count(new Wrapper()) ``` ### getObject 查询符合条件的记录 返回值: 查询结果,一个对象类型的数组 | 入参 | 说明 | |----------------|------| | wrapper: Wrapper | 搜索条件 | | 返回值 | 说明 | |-------------|------| | AnyObject[] | 对象数组 | ```typescript // 查询name等于111的数据 const objList = await this.empMapper.getObject(new Wrapper().eq('name', '111')) ``` ### getObjectBySql 通过SQL,查询符合条件的记录 返回值: 查询结果,一个对象类型的数组 | 入参 | 说明 | |-------------------------------------|-------| | sql: string | sql语句 | | params: relationalStore.ValueType[] | 占位符参数 | | 返回值 | 说明 | |-------------|------| | AnyObject[] | 对象数组 | ```typescript // 统计行数 const res = await this.mapper.getObjectBySql('select count(*) from t_emp', []) ``` ### getList 查询符合条件的记录 返回值: 查询结果,泛型T的数组 | 入参 | 说明 | |----------------|------| | wrapper: Wrapper | 搜索条件 | | 返回值 | 说明 | |-----|--------| | T[] | 泛型T的数组 | ```typescript // 查询name等于111的数据,返回数组 const list = await this.empMapper.getList(new Wrapper().eq('name', '111')) ``` ### getPage 分页查询符合条件的记录 返回值:分页查询结果,包含记录列表和总记录数 | 入参 | 说明 | |----------------|------| | wrapper: Wrapper | 搜索条件 | | 返回值 | 说明 | |---------|--------| | Page | 分页查询结果 | | Page | 说明 | |---------|------| | total | 总数 | | current | 当前页 | | size | 每页数量 | | record | 结果集 | ```typescript // 查询第一页的数据,返回Page对象 const page = await this.empMapper.getPage(1, 10) // 总数 const total = page.total // 当前页 const current = page.current // 每页条数 const size = page.size // 结果集 const record = page.record ``` ### getById 根据主键ID查询 返回值: 查询结果,可能是undefined或泛型T的对象 | 入参 | 说明 | |---------------|-----| | id: ValueType | 主键值 | | 返回值 | 说明 | |----------------|-----------------------| | T 或 undefined | 存在返回结果,不存在返回undefined | ```typescript // 查询主键ID等于14的数据 this.empMapper.getById(14) ``` ### insert 插入一条记录 返回值:void,失败则抛出异常 注:若插入的某个字段为空,可以设置为 `undefined或null` | 入参 | 说明 | |--------|----------| | obj: T | 一个泛型T的对象 | | 返回值 | 说明 | |------|----------| | void | 执行失败抛出异常 | ```typescript const emp = new Employee() emp.name = '新添加的' // 如果是自增,id可以不用赋值 this.empMapper.insert(emp) ``` ### updateById 根据 ID ,更新符合条件的记录 返回值:void,失败则抛出异常 注:不想更新的字段必须设置为`undefined`,其他值包括null,都会更新到数据库 | 入参 | 说明 | |--------|-----------------| | obj: T | 一个泛型T的对象,主键不能为空 | | 返回值 | 说明 | |------|----------| | void | 执行失败抛出异常 | ```typescript const emp = new Employee() emp.id = 20 emp.name = '张三' // 根据主键id修改 this.empMapper.updateById(emp) ``` ### update 通过指定条件更新数据 返回值:void,失败则抛出异常 | 入参 | 说明 | |----------------|------| | wrapper: Wrapper | 更新条件 | | 返回值 | 说明 | |------|----------| | void | 执行失败抛出异常 | ```typescript // 将name等于张三的数据,改为name等于李四 this.empMapper.update(new Wrapper().set('name', '李四').eq('name', '张三')) ``` ### deleteById 根据 ID ,删除数据 返回值:void,失败则抛出异常 | 入参 | 说明 | |---------------|-----| | id: ValueType | 主键值 | | 返回值 | 说明 | |------|----------| | void | 执行失败抛出异常 | ```typescript // 删除主键ID等于5的数据 this.empMapper.deleteById(5) ``` ### delete 通过指定条件删除数据 返回值:void,失败则抛出异常 | 入参 | 说明 | |----------------|------| | wrapper: Wrapper | 删除条件 | | 返回值 | 说明 | |------|----------| | void | 执行失败抛出异常 | ```typescript // 删除name等于111的数据 this.empMapper.delete(new Wrapper().eq('name', '111')) ``` ### getConnection 获取一个数据库的连接对象`Connection`,可以直接进行SQL语句的调用 参考上面的`建表、连接查询等复杂SQl,采用手写SQL方法` #### init 获取一个新的数据库连接 | 入参 | 说明 | |-------------------------------------|-------| | config: relationalStore.StoreConfig | 数据库配置 | | 返回值 | 说明 | |------------|--------------| | Connection | Connection对象 | #### execDML `execDML`是数据操纵函数(DML),执行创建、添加、修改语句 | 入参 | 说明 | |-------------|-------------------------------------| | sql: string | SQL语句 | | params | 数组,SQL语句中参数的值。该值与sql参数语句中的`?`占位符相对应 | | 返回值 | 说明 | |------|----------| | void | 执行失败抛出异常 | #### execDQL `execDQL`是数据查询函数,执行查询语句 | 入参 | 说明 | |-------------|-------------------------------------| | sql: string | SQL语句 | | params | 数组,SQL语句中参数的值。该值与sql参数语句中的`?`占位符相对应 | | 返回值 | 说明 | |---------------------------|-------------| | relationalStore.ResultSet | 返回ResultSet | #### beginTransaction 开启事务 #### commit 提交事务 #### rollBack 回滚事务 #### close 关闭当前的Connection连接 ## 条件构造器介绍 ### set 在update语句中,用于指定要修改的列及其新值 ```typescript // 更新`name`等于`张三`的数据,将`name`值更改为`李四` new Wrapper() .set('name', '李四') .eq('name', '张三') ``` ### eq 等于 ```typescript // 查询`name`等于`王二`,并且`age`为`18`的数据 new Wrapper().eq('name', '王二').eq('age', 18) ``` ### notEq 不等于 ```typescript // 查询`name`不等于`王二`的数据 new Wrapper().notEq('name', '王二') ``` ### lt 小于 ```typescript // 查询`age`小于`50`的数据 new Wrapper().lt('age', 50) ``` ### lte 小于等于 ```typescript // 查询`age`小于等于`50`的数据 new Wrapper().lte('age', 50) ``` ### gt 大于 ```typescript // 查询`age`大于`50`的数据 new Wrapper().gt('age', 50) ``` ### gte 大于等于 ```typescript // 查询`age`大于等于`50`的数据 new Wrapper().gte('age', 50) ``` ### in 设置单个字段的值,在给定的集合中 ```typescript // 查询`age`在`18、19、20`之中的数据 new Wrapper().in('age', [18, 19, 20]) ``` ### notIn 设置单个字段的值,不在给定的集合中 ```typescript // 查询`age`不在`18、19、20`之中的数据 new Wrapper().notIn('age', [18, 19, 20]) ``` ### between 设置单个字段的 BETWEEN 条件 ```typescript // 查询 age 在 18-60之间的数据 new Wrapper().between('age', 18, 60) ``` ### notBetween 设置单个字段的 NOT BETWEEN 条件 ```typescript // 查询 age 不在 18-60之间的数据 new Wrapper().notBetween('age', 18, 60) ``` ### like 单个字段的模糊匹配条件 ```typescript // 匹配 `name` 的第一个字是`张`的数据 new Wrapper().like('name', '张%') ``` ### notLike 单个字段的非模糊匹配条件 ```typescript // 匹配 `name` 的第一个字,不是`张`的数据 new Wrapper().notLike('name', '张%') ``` ### isNull 单个字段为null ```typescript // 查询 `title` 等于null的数据 new Wrapper().isNull('title') ``` ### isNotNull 单个字段不为null ```typescript // 查询 `title` 不等于null的数据 new Wrapper().isNotNull('title') ``` ### orderByAsc 将查询结果根据某字段进行升序排序,可以多次调用,按顺序拼接order内容 ```typescript // 将查询结果根据id升序排列 new Wrapper().orderByAsc('id') ``` ### orderByDesc 将查询结果根据某字段进行降序排序,可以多次调用,按顺序拼接order内容 ```typescript // 将查询结果根据id降序排列 new Wrapper().orderByDesc('id') ``` ### groupBy 设置查询结果的分组条件。通过指定一个或多个字段 ```typescript // 依次根据id、name进行分组 new Wrapper().groupBy('id', 'name') ``` 生成的sql ``` SELECT * FROM user GROUP BY id, name ``` ### having 设置 HAVING 子句,过滤分组后的结果。通常与 GROUP BY 一起使用,用于对分组后的数据进行条件筛选 ```typescript // 根据name分组,过滤分组条件是name不等于张三 new Wrapper().groupBy('name').having(`name != '张三'`) ``` 生成的sql ``` SELECT * FROM user GROUP BY name HAVING name != '张三' ``` ### or 用于在查询条件中添加 OR 逻辑。通过调用 or 方法,可以改变后续查询条件的连接方式,从默认的 AND 连接变为 OR 连接 ```typescript new Wrapper().eq('name', '111') .or(new Wrapper().eq('name', '222').eq('age', 18)) ``` 生成SQL为 ``` name = '111' or ( name = '222' and age=18 ) ``` ### and 用于在查询条件中添加 AND 逻辑。通过调用 and 方法,可以创建 AND 嵌套条件,即在一个 AND 逻辑块中包含多个查询条件 ```typescript new Wrapper() .eq('name', '111') .and(new Wrapper().notEq('name', '222')) ``` 生成SQL为 ``` name = '111' and ( name != '222' ) ``` ### select 默认查询为: `select *` 调用select函数,将默认的 `*` 更改为指定内容 注:若添加了额外内容,例如聚合函数、字段别名,建议使用`getObject`方法,将结果从对象中取出(getObjectBySql也可以) ```typescript //指定字段 new Wrapper().select('id,name,age') //使用函数 new Wrapper().select('count(*),sum(age)') //字段别名 new Wrapper().select('age as nianling') ``` ## 其他功能 ### 多数据源 参考如下示例 ```typescript import { Employee } from '../entity/Employee' import { relationalStore } from '@kit.ArkData' import { BaseMapper, MapperParam } from 'rdbplus' // 实现一个 getRow const getRow = (res: relationalStore.ResultSet) => { const emp = new Employee() emp.id = res.getLong(res.getColumnIndex('id')) emp.name = res.getString(res.getColumnIndex('name')) return emp } export class EmpMapper extends BaseMapper { // 构造函数,仅接收参数,将参数传给super private constructor(config: relationalStore.StoreConfig) { super({ tableName: 't_emp', primaryKey: 'id' }, getRow, config) } // 手动 new出EmpMapper // 数据库1 static getDemo1DB(): EmpMapper { return new EmpMapper( { name: 'Demo1DB.db', securityLevel: relationalStore.SecurityLevel.S1 } ) } // 数据库2 static getDemo2DB(): EmpMapper { return new EmpMapper( { name: 'Demo2DB.db', securityLevel: relationalStore.SecurityLevel.S1 } ) } } ``` ### 事务 参考如下代码 ``` import { Employee } from '../entity/Employee' import { EmpMapper } from '../mapper/EmpMapper' @Entry @Component struct Index { empMapper = new EmpMapper() build() { Row() { Button('事务成功').onClick(async (event: ClickEvent) => { // 获取一个db连接 const db = await this.empMapper.getConnection() try { //开启事务 db.beginTransaction() const emp = new Employee() emp.name = '事务' // 将 db 传进去,保持所有操作在同一连接上 this.empMapper.insert(emp, db) //提交事务 db.commit() } catch (e) { // 回滚 db.rollBack() } finally { // 关闭连接 db.close() } }) Button('事务失败').onClick(async (event: ClickEvent) => { // 获取一个db连接 const db = await this.empMapper.getConnection() try { db.beginTransaction() const emp = new Employee() emp.name = '事务失败' // 将 db 传进去,保持所有操作在同一连接上 this.empMapper.insert(emp, db) // 抛出异常,回滚事务 throw new Error() //提交事务 db.commit() } catch (e) { // 回滚 db.rollBack() } finally { // 关闭连接 db.close() } }) } } } ``` ## 贡献代码 使用过程中发现任何问题都可以提 `Issue`,也欢迎您发 `PR` https://gitee.com/yongoe/RdbPlus https://github.com/yongoe1024/RdbPlus `(以github为主)` ## 开源协议 本项目基于 [MIT License](https://mit-license.org)