【鸿蒙优选三方库】@ohos/dataorm:注解定义实体,告别手写 SQL
【鸿蒙优选三方库】@ohos/dataorm:注解定义实体,告别手写 SQL
手写 SQL 的痛:建表语句拼字符串、JOIN 查询拼字符串、结果集手动映射成对象、字段改名要全局搜替换。
@ohos/dataorm用注解定义实体,链式 API 拼查询,字段名写错编译期就报错。
📦 仓库地址 |
ohpm install @ohos/dataorm| v2.3.10-rc.1 | Apache-2.0
它解决什么问题
鸿蒙做本地持久化通常用关系型数据库(RDB)。但直接用原生 RDB Store:
- 建表、查询、更新全是 SQL 字符串拼接,易错难维护
- 表结构变更要手动写迁移
- 实体 ↔ 数据库映射全部手写
- 一对多、多对一关联查询写起来痛苦
- 批量操作没有统一封装
@ohos/dataorm 把 Java 圈最成熟的 greenDAO 思路搬进 ArkTS:注解声明模型 → 生成 Dao → 链式 API 查询,还内置迁移、监听、缓存、批量操作工具。
核心能力
| 能力 | 说明 |
|---|---|
| 注解式实体 | @Entity @Id @NotNull @Unique @Index |
| 完整关联 | @ToMany @ToOne @JoinEntity @OrderBy |
| 类型转换 | @Convert 自定义类型 ↔ 数据库值 |
| 嵌套对象 | @Embedded @Transient @Union |
| 链式查询 | .where().eq().and().like().orderDesc().list() |
| QueryBuilder 高级 | 去重、JOIN、分页、计数、排序 |
| 数据库迁移 | Migration API 平滑升级表结构 |
| 数据监听 | 表/库级变更回调 |
| 多数据库 | 单应用多个独立库并存 |
| Async/Sync | 统一命名后缀,约定清晰 |
30 秒上手
import { Entity, Id, NotNull, Column, ToMany, List } from '@ohos/dataorm'
@Entity({ tableName: 'NOTE' })
export class Note {
@Id() @Column({ columnName: 'ID' }) id: number = 0
@NotNull() @Column({ columnName: 'TEXT' }) text: string = ''
@Column({ columnName: 'DATE' }) date: number = 0
}
import { DataORM } from '@ohos/dataorm'
const db = DataORM.init({ name: 'app.db', version: 1, entities: [Note] })
const noteDao = db.dao(Note)
// 增
const note = new Note()
note.text = 'Hello HarmonyOS'
note.date = Date.now()
await noteDao.insert(note)
// 查(链式,类型安全)
const list = await noteDao.queryBuilder()
.where(Note.TEXT.like('%Hello%'))
.orderDesc(Note.DATE)
.list()
// 改
note.text = 'Updated'
await noteDao.update(note)
// 删
await noteDao.deleteById(note.id)
关联查询——@ToMany 让关联对象自动带出,不用手写 JOIN:
@Entity({ tableName: 'USER' })
class User {
@Id() @Column({ columnName: 'ID' }) id: number = 0
@Column({ columnName: 'NAME' }) name: string = ''
@ToMany({ joinEntity: Order.class }) orders: List<Order> = new List()
}
// 查用户时订单自动带出
const user = await userDao.queryBuilder().where(User.ID.eq(1)).unique()
console.info(user.orders.length) // 无需第二次查询
社区已解决的典型问题
异步批量插入大数据量时 await 不等待(#301)
saveInTxAsync 在数据量大时 await 无法同步等待,代码继续往下跑但插入还没完成——后续查询查不到全部数据。Issue 已由维护者处理。业务侧对大批量插入仍建议分批 + 回查校验,分批还能顺带做进度反馈。
数据库升级接口不能回滚(#302)
升级时接口内部捕获了异常,导致事务不能回滚,数据库卡在损坏的中间状态:表结构改了一半,老数据读不出、新数据写不进。已修正异常传播与回滚逻辑。业务侧仍应自己做「备份 → 迁移 → 失败恢复」三步保护。
复合主键示例有误导性(#305 / #307)
Demo 中 CompositePrimaryKey.ets 与 UnionPrimaryKey.ets 的文件名、按钮文案与实际功能存在歧义。已修正。学复合主键用法时以 API.md 官方文档为准,Demo 需交叉验证。
Query 测试用例不足(#298)
社区已补充查询场景的测试覆盖。复杂查询(JOIN、分页、去重)建议在自己项目里先小范围验证行为符合预期。
两个必须遵守的实践
1. 迁移脚本三条铁律
| 原则 | 说明 |
|---|---|
| 幂等 | 可重复执行不出错(用 IF NOT EXISTS) |
| 只增不减 | 新增字段安全,删字段/删表要极度谨慎 |
| 可回滚 | 迁移前必备份,失败能恢复 |
2. 高频查询字段加索引
@Index() @Column({ columnName: 'USER_ID' }) userId: number = 0
数据量小的时候感觉不到差别,积累到几万条后没索引的查询会明显变慢。
适合谁用
- 本地数据存储:用户信息、设置、配置、缓存
- 业务实体持久化:订单、商品、文章、聊天记录、打卡记录
- 复杂关联模型:一对多、多对一、多对多
- 需要版本迁移:迭代时表结构平滑升级
- 需要监听变化:跨页面/跨组件响应数据变更
- 想少写 SQL:代码量减少一个数量级
297 个 Issue 已关闭。数据库是最不能出错的一层——丢一次数据,用户就再也不回来了。这个库的 Issue 列表里,事务和迁移相关的讨论特别值得读一遍。
更多推荐



所有评论(0)