An open API service indexing awesome lists of open source software.

https://github.com/yongoe1024/RdbPlus

一个sqlite的增强工具,无需编写sql代码,一行搞定增删改查,为简化开发、提高效率而生
https://github.com/yongoe1024/RdbPlus

arkts harmonyos sqlite util

Last synced: 9 months ago
JSON representation

一个sqlite的增强工具,无需编写sql代码,一行搞定增删改查,为简化开发、提高效率而生

Awesome Lists containing this project

README

          

# rdb-plus 使用文档

## 简介

这是一个sqlite的增强工具,无需编写sql代码,通过继承BaseMapper类,一行搞定增删改查,为简化开发、提高效率而生。(类似Mybatis-plus)

## 版本说明
### 2.0.1
1. 优化生成的sql

### 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)