#跟着晓明学鸿蒙# 鸿蒙Next开发中ohpm三方库使用指南之zxhhyj-storm集成示例

前言
随着鸿蒙生态蓬勃发展,各种实用的鸿蒙三方库已成为开发者高效开发的利器。它不仅能拓展应用功能边界,还能大幅提升开发效率,不管你是刚接触鸿蒙开发的小白,还是开发时长两年半的老司机,掌握这些三方库,绝对能让你在鸿蒙开发上更加方便!博主在日常的工作开发中也也是如此,今天给大家分享的是storm三方库。
介绍
Storm是直接基于纯TypeScript编写的高效简洁的轻量级OpenHarmonyOS SQL ORM框架,提供了强类型的SQL DSL,直接将低级bug暴露在编译期。
安装
在命令行中执行以下命令。
ohpm install @zxhhyj/storm
快速上手
这里以用户表和用户个人信息表为示例:
1.定义用户信息表
创建UserInfo.(ts/ets)
文件,并实现以下代码:
export interface UserInfo {
id?: number
name: string
info: string
}
export class UserInfoTable extends Table<UserInfo> {
override readonly tableName = 't_user_info'
readonly id = Column.integer('id').primaryKey(true).bindTo(this, 'id')
readonly name = Column.text('name').notNull().bindTo(this, 'name')
readonly info = Column.text('info').bindTo(this, 'info')
}
export const userInfoTable = new UserInfoTable()
其中UserInfo
为实体模型,UserInfoTable
为表的Scheme
,在其中使用Column
API来定义列,最后导出表实例。
2.定义用户表
创建User.(ts/ets)
文件,并实现以下代码:
export interface User {
id?: number
email: string
userInfo: UserInfo
createDataTime: Date
}
export class UserTable extends Table<User> {
readonly tableName = 't_user'
readonly id = Column.integer('id').primaryKey(true).bindTo(this, 'id')
readonly email = Column.text('email').notNull().bindTo(this, 'email')
readonly userInfo = Column.references('user_info_id', userInfoTable).bindTo(this, 'userInfo')
readonly createDataTime = Column.date('create_data_time').notNull().bindTo(this, 'createDataTime')
}
export const userTable = new UserTable()
这里使用了Column.references
API将UserTable
和UserInfoTable
进行关联。
3.定义并初始化数据库
创建AppDatabase.(ts/ets)
文件,并实现以下代码:
class AppDatabase extends Database {
readonly userDao = DatabaseDao.form(this).select(userTable)
readonly userInfoDao = DatabaseDao.form(this).select(userInfoTable)
protected initDb(context: Context) {
return relationalStore.getRdbStore(context, { name: 'app.db', securityLevel: relationalStore.SecurityLevel.S1 })
}
protected onCreate(_rdbStore: RdbStoreWrapper): void {
}
protected onDatabaseCreate(rdbStore: RdbStoreWrapper): void {
rdbStore.executeSync(SQL.createTableAndIndex(userTable))
rdbStore.executeSync(SQL.createTableAndIndex(userInfoTable))
}
}
export const appDatabase = new AppDatabase(1)
这段代码的大致作用:
- 你需要重写
initDb
函数并在其中返回你的relationalStore.getRdbStore
。 onCreate
将在数据库完全初始化完毕时被调用(一般在onDatabaseCreate
之后)。onDatabaseCreate
将在你的RdbStore
版本为0
时被调用,你需要在这里执行初始化你的数据库,执行完毕后版本号为Database
构造函数中输入的整数值。
最后需要调用init
函数进行初始化:
export class AppAbilityStage extends AbilityStage {
async onCreate() {
await appDatabase.init(this.context)
}
onAcceptWant(_want: Want): string {
return 'AppAbilityStage'
}
}
4.使用 DatabaseDao 访问 Database
1.使用 insert
API 插入数据
const userInfo: UserInfo = {
name: '浩',
info: '无限进步'
}
const user: User = {
email: 'zxhhyj@qq.com',
userInfo: userInfo,
createDataTime: new Date(),
}
await appDatabase.userInfoDao.insert(userInfo)
await appDatabase.userDao.insert(user)
// insert API存在一个副作用,插入成功后会自动将自增主键回写到实体中
// 另外在表中存在 references 列时,建议开启事务,且需要保证 references 列的插入删除先于它的所在的表
// 就像示例中一样,先插入 userInfo 再插入 user
2.使用 delete
& deleteIf
API 删除数据
const userInfo: UserInfo = {
name: '浩',
info: '无限进步'
}
await appDatabase.userInfoDao.insert(userInfo)
// 使用实体,需要在插入后使用或者确保其中存在主键
await appDatabase.userInfoDao.delete(userInfo)
// 使用谓词
await appDatabase.userInfoDao.deleteIf(it => it.equalTo(userInfoTable.id, 1))
3.使用 update
& updateIf
API 更新数据
const userInfo: UserInfo = {
name: '浩',
info: '无限进步'
}
await appDatabase.userInfoDao.insert(userInfo)
userInfo.name = '真的科幻吗?'
// 使用实体,需要在插入后使用或者确保其中存在主键
await appDatabase.userInfoDao.update(userInfo)
// 使用谓词
await appDatabase.userInfoDao.updateIf(it => it.equalTo(userInfoTable.id, 1), {
info: '真的无限进步吗?'
})
4.使用 query
& queryOne
API 查询数据
查询多个数据:
const userInfos = await appDatabase.userInfoDao.query(it => it.limitAs(30))
for (const userInfo of userInfos) {
console.log(userInfo.name)
}
查询单个数据:
const bookcase = await appDatabase.bookcaseDao.queryOne(it => it.isNotNull(bookTable.name))
5.使用 toList
& first
API 查询数据
查询多个数据:
const userInfos = await appDatabase.userInfoDao.toList(it => it.limitAs(30))
const haoUser = userInfos.find(item => item.name === '浩')
// 设置模型字段值并立即更新至数据库中对应的记录
await haoUser?.set('name', 'Hao')
// 立即将模型的所有字段更新至数据库中对应的记录
await haoUser?.flushChanges()
// 删除当前模型在数据库中对应的记录
await haoUser?.delete()
查询单个数据:
const haoUser = await appDatabase.userInfoDao.first(it => it.equalTo(userInfoTable.name, '浩'))
// 设置模型字段值并立即更新至数据库中对应的记录
await haoUser?.set('name', 'Hao')
// 立即将模型的所有字段更新至数据库中对应的记录
await haoUser?.flushChanges()
// 删除当前模型在数据库中对应的记录
await haoUser?.delete()
5.使用 launchTransaction
API 开启事务
// 底层使用 beginTransaction
appDatabase.launchTransaction(async it => {
const haoUser = await it.userInfoDao.first(it => it.equalTo(userInfoTable.name, '浩'))
await haoUser.set('name', 'Hao')
})
// 底层使用 beginTrans
appDatabase.userInfoDao.launchTransaction(async it => {
const haoUser = await it.first(it => it.equalTo(userInfoTable.name, '浩'))
await haoUser.set('name', 'Hao')
})
5.使用 SQLiteDatabase 访问 Database
SQLiteDatabase
相比DatabaseDao
,API自由度更高,不局限与操作单个表,适合需要一次操作多个表时使用。
1.在 Database 中声明
以之前的AppDatabase
为例:
class AppDatabase extends Database {
readonly userDao = DatabaseDao.form(this).select(userTable)
readonly userInfoDao = DatabaseDao.form(this).select(userInfoTable)
// 声明 SQLiteDatabase
readonly db = SQLiteDatabase.form(this)
protected initDb(context: Context) {
return relationalStore.getRdbStore(context, { name: 'app.db', securityLevel: relationalStore.SecurityLevel.S1 })
}
protected onCreate(_rdbStore: RdbStoreWrapper): void {
}
protected onDatabaseCreate(rdbStore: RdbStoreWrapper): void {
rdbStore.executeSync(SQL.createTableAndIndex(userTable))
rdbStore.executeSync(SQL.createTableAndIndex(userInfoTable))
}
}
export const appDatabase = new AppDatabase(1)
SQLiteDatabase
的增删改API与DatabaseDao
使用基本一致,这里不再赘述,但查询的API比较特殊。
2. 使用 querySql
查询数据
简单示例:
// 查询 email 不为空的数据
const emails = await appDatabase.db.querySql(SQL
.select(userTable.id, userTable.email)
.form(userTable)
.where()
.isNull(userTable.email))
for (const emailsElement of emails) {
// 同时类型被推断为
// {
// id: number;
// email: string;
// }
console.log(`${emailsElement.id}`)
console.log(emailsElement.email)
}
6.使用 Dao 封装 Database 的访问
有些时候我们需要对数据库的访问进行封装。
创建UserDao.(ts/ets)
文件,并实现以下代码:
export class UserDao extends Dao {
// 将 userDao & userInfoDao 私有化
private readonly userDao = DatabaseDao.form(this).select(userTable)
private readonly userInfoDao = DatabaseDao.form(this).select(userInfoTable)
/**
* 供外部调用
*/
createUser() {
appDatabase.launchTransaction(async () => {
const userInfo: UserInfo = {
name: '浩',
info: '无限进步'
}
const user: User = {
email: 'zxhhyj@qq.com',
userInfo: userInfo,
createDataTime: new Date(),
}
await this.userInfoDao.insert(userInfo)
await this.userDao.insert(user)
})
}
}
然后在Database
中声明。
以之前的AppDatabase
为例:
class AppDatabase extends Database {
// 声明 UserDao
readonly userDao = Dao.form(this).select(UserDao)
protected initDb(context: Context) {
return relationalStore.getRdbStore(context, { name: 'app.db', securityLevel: relationalStore.SecurityLevel.S1 })
}
protected onCreate(_rdbStore: RdbStoreWrapper): void {
}
protected onDatabaseCreate(rdbStore: RdbStoreWrapper): void {
rdbStore.executeSync(SQL.createTableAndIndex(userTable))
rdbStore.executeSync(SQL.createTableAndIndex(userInfoTable))
}
}
export const appDatabase = new AppDatabase(1)
7.升级数据库
例如,需要对前文的UserTable
中新增一个字段password
:
export interface User {
id?: number
email: string
// 新增 password
password: string
userInfo: UserInfo
createDataTime: Date
}
export class UserTable extends Table<User> {
readonly tableName = 't_user'
readonly id = Column.integer('id').primaryKey(true).bindTo(this, 'id')
readonly email = Column.text('email').notNull().bindTo(this, 'email')
// 新增 password
readonly password = Column.text('password').notNull().bindTo(this, 'password')
readonly userInfo = Column.references('user_info_id', userInfoTable).bindTo(this, 'userInfo')
readonly createDataTime = Column.date('create_data_time').notNull().bindTo(this, 'createDataTime')
}
export const userTable = new UserTable()
然后修改AppDatabase
:
class AppDatabase extends Database {
readonly userDao = DatabaseDao.form(this).select(userTable)
readonly userInfoDao = DatabaseDao.form(this).select(userInfoTable)
protected initDb(context: Context) {
return relationalStore.getRdbStore(context, { name: 'app.db', securityLevel: relationalStore.SecurityLevel.S1 })
}
protected onCreate(_rdbStore: RdbStoreWrapper): void {
}
protected onDatabaseCreate(rdbStore: RdbStoreWrapper): void {
rdbStore.executeSync(SQL.createTableAndIndex(userTable))
rdbStore.executeSync(SQL.createTableAndIndex(userInfoTable))
}
/**
* 定义迁移函数,并使用 @Migration 注解,参数分别为起始版本号和结束版本号
*/
@Migration(1,2)
protected migration_1_2(rdbStore: RdbStoreWrapper) {
// 执行迁移逻辑
rdbStore.executeSync(SQL.alterTable(userTable, sql => sql.addColumn(userTable.password)))
}
}
// 修改最新版本
export const appDatabase = new AppDatabase(2)
这下就大功告成了。
8.快速生成 SQL
可以使用SQL
API来快速生成SQL语句:
// 生成创建表和索引的SQL语句
SQL.createTableAndIndex(userInfoTable)
// 生成创建表的SQL语句
SQL.createTable(userInfoTable)
// 生成创建索引的SQL语句
SQL.createIndex(userInfoTable)
// 生成新增列的SQL语句
SQL.alterTable(userInfoTable, sql => sql.addColumn(userInfoTable.info))
// 生成更新列的SQL语句
SQL.alterTable(userInfoTable, sql => sql.alterColumn(userInfoTable.info))
9.多数据源
参考定义并初始化数据库,创建不同的Database
并修改initDb
返回的RdbStore
即可。
更多推荐
所有评论(0)