28_ValuesBucket数据插入与实体映射
28 ValuesBucket 数据插入与实体映射
前言

图:28 ValuesBucket 数据插入与实体映射 运行效果截图(HarmonyOS NEXT)
在鸿蒙 RDB 中,数据的插入通过 store.insert(table, values) 方法完成,其中 values 的类型是 ValuesBucket——本质上是一个键值对映射,键是列名,值是要插入的数据。
本文将深入解析 ValuesBucket 的构建方法、实体→ValuesBucket 的映射模式,以及项目 5 个 DAO 中 insert 操作的最佳实践。
鸿蒙官方·ValuesBucket 文档:developer.huawei.com
项目源码仓库:harmony-app GitHub

图:实体对象 → ValuesBucket → RDB Insert 完整流程
一、ValuesBucket 基础
1.1 定义
ValuesBucket 是一个类型别名,本质上是 Record<string, number | string | boolean | Uint8Array | null>:
// 类型定义
type ValuesBucket = Record<string, number | string | boolean | Uint8Array | null>
// 最简单的使用
const values: relationalStore.ValuesBucket = {
open_id: 'local',
name: '鹿鹿',
created_at: Date.now()
}
1.2 值类型对照表
| ArkTS 类型 | ValuesBucket 类型 | 插入到 SQL 中的类型 | Getter |
|---|---|---|---|
number |
number |
INTEGER / REAL |
getLong() / getDouble() |
string |
string |
TEXT |
getString() |
boolean |
boolean |
INTEGER(0/1) |
getLong() |
Uint8Array |
Uint8Array |
BLOB |
getBlob() |
null/undefined |
不传或 null |
NULL |
判断后再读取 |
二、各 DAO 的 ValuesBucket 构建
2.1 UserDao:最简单的插入
// UserDao.ensureLocalUser
const values: relationalStore.ValuesBucket = {
open_id: 'local',
name: name, // 方法参数传入
created_at: Date.now()
}
const id = await store.insert(TABLE, values)
特点:只插入 3 个字段,其余字段(avatar)有 DEFAULT 值或不填。
2.2 HandwritingDao:完整的字段映射
static async create(item: HandwritingEntity): Promise<number> {
const store = HandwritingDao.getStore()
const values: relationalStore.ValuesBucket = {
archive_id: item.archive_id ?? 0, // ?? 处理可选字段
source: item.source, // NOT NULL 字段
image_path: item.image_path ?? '',
ocr_text: item.ocr_text ?? '',
feature_json: item.feature_json ?? '',
energy_score: item.energy_score ?? 0,
device_type: item.device_type ?? '',
word_count: item.word_count ?? 0,
write_duration: item.write_duration ?? 0,
created_at: item.created_at
}
return await store.insert(TABLE, values)
}
2.3 各 DAO 的 insert 映射对比
| DAO | 插入字段数 | 必传字段 | 可选字段处理 |
|---|---|---|---|
| UserDao | 3 | open_id, name, created_at | 不传(SQL DEFAULT) |
| HandwritingDao | 10 | source, created_at | ?? 提供默认值 |
| ArchiveDao | 8 | user_id, name, created_at | ?? '' 或 ?? 0 |
| ReportDao | 7 | handwriting_id, created_at | ?? '' 或 ?? 0 |
| RelationDao | 5 | user_id, partner_archive_id, created_at | ?? '' 或 ?? 0 |
三、字段默认值策略
3.1 ?? 操作符的应用
项目中大量使用 ?? 空值合并操作符来处理可选字段:
const values: relationalStore.ValuesBucket = {
archive_id: item.archive_id ?? 0, // undefined → 0(未归类)
image_path: item.image_path ?? '', // undefined → 空字符串
ocr_text: item.ocr_text ?? '',
feature_json: item.feature_json ?? '',
energy_score: item.energy_score ?? 0,
device_type: item.device_type ?? '', // v2 新增字段
word_count: item.word_count ?? 0,
write_duration: item.write_duration ?? 0,
}
?? 与 || 的区别:
| 操作符 | false |
0 |
'' |
undefined |
null |
|---|---|---|---|---|---|
a ?? 'default' |
false |
0 |
'' |
'default' |
'default' |
a || 'default' |
'default' |
'default' |
'default' |
'default' |
'default' |
提示:
??只会在undefined或null时使用默认值。这对于数值字段(如energy_score: 0)非常重要——||会把0当作 falsy 值替换成默认值,而??正确保留0。
3.2 各字段类型的默认值
| SQL 类型 | ArkTS 类型 | ValuesBucket 默认值 | 含义 |
|---|---|---|---|
| INTEGER (外键) | number | undefined |
0 |
0 表示"未关联" |
| INTEGER (数值) | number | undefined |
0 |
无数据 |
| TEXT (内容) | string | undefined |
'' |
空字符串 |
| TEXT (路径) | string | undefined |
'' |
无路径 |
| INTEGER (NOT NULL) | number(必传) |
无默认值 | 必须传入有效值 |
四、insert 的返回值
4.1 返回值说明
const id = await store.insert(TABLE, values)
// 返回值类型:number
// 成功时:新插入行的 row ID(自增主键值)
// 失败时:抛出异常
4.2 典型用法
// HandwritingDao.create — 返回 ID,供后续创建报告使用
const handwritingId = await HandwritingDao.create({
archive_id: archiveId,
source: 'camera',
ocr_text: ocrText,
feature_json: JSON.stringify(features),
created_at: Date.now()
})
// 使用返回的 ID 创建关联报告
await ReportDao.create({
handwriting_id: handwritingId,
archive_id: archiveId,
personality_type: result.type,
radar_json: JSON.stringify(result.scores),
created_at: Date.now()
})
五、预置的 ValuesBucket 模式
5.1 直接字面量
// UserDao — 简单,字段少
const id = await store.insert(TABLE, {
open_id: 'local',
name,
created_at: Date.now()
})
5.2 从 Entity 映射
// HandwritingDao — 从 HandwritingEntity 对象映射
const values: relationalStore.ValuesBucket = {
archive_id: item.archive_id ?? 0,
source: item.source,
image_path: item.image_path ?? '',
// ...
}
5.3 手动构造
// DemoSeeder — 在循环中构造
const handwritingId = await HandwritingDao.create({
archive_id: archiveId,
source: 'demo',
image_path: '',
ocr_text: p.text,
feature_json: JSON.stringify(p.scores),
energy_score: p.scores.energy,
created_at: timestamp
})
六、写入性能优化建议
6.1 批量插入
当需要插入多条记录时,逐条 insert 效率较低:
// ❌ 逐条插入(DemoSeeder 中目前的方式)
for (const item of items) {
await HandwritingDao.create(item)
}
// ✅ 批量插入优化(使用事务)
static async batchCreate(items: HandwritingEntity[]): Promise<void> {
const store = HandwritingDao.getStore()
await store.executeSql('BEGIN TRANSACTION')
try {
for (const item of items) {
const values: relationalStore.ValuesBucket = {
/* ... */
}
await store.insert(TABLE, values)
}
await store.executeSql('COMMIT')
} catch (e) {
await store.executeSql('ROLLBACK')
throw e
}
}
6.2 事务性能对比
| 方式 | 插入 10 条 | 插入 100 条 | 说明 |
|---|---|---|---|
| 逐条插入(无事务) | ~50ms | ~500ms | 每条 commit 一次 |
| 显式事务 | ~5ms | ~30ms | 一次 commit |
| 批量 prepare(SQLite) | ~3ms | ~20ms | 最优化 |
七、常见错误排查
7.1 字段名不匹配
// ❌ 错误:字段名拼写错误
const values: relationalStore.ValuesBucket = {
create_at: Date.now() // 拼写错误:应该是 created_at
}
// SQLite 会静默忽略不存在的列名
// ✅ 正确:与 CREATE TABLE 中的列名完全一致
const values: relationalStore.ValuesBucket = {
created_at: Date.now()
}
7.2 NOT NULL 约束冲突
// ❌ 错误:NOT NULL 字段没有传入
const values: relationalStore.ValuesBucket = {}
await store.insert(TABLE, values)
// → 抛出异常:"NOT NULL constraint failed: user.name"
// ✅ 正确:所有 NOT NULL 字段都有值
const values: relationalStore.ValuesBucket = {
source: 'camera',
created_at: Date.now()
}
7.3 类型不匹配
// ❌ 错误:类型不匹配
const values: relationalStore.ValuesBucket = {
name: 12345 // SQL 中 name 是 TEXT,但传了 number
}
// ✅ 正确:类型一致
const values: relationalStore.ValuesBucket = {
name: '鹿鹿' // string → TEXT
}
八、协议层映射总结
8.1 数据的完整生命周期
// ① ArkTS 页面得到用户输入
const capturedText = '今天醒得很早...'
// ② 构造 Entity 对象
const entity: HandwritingEntity = {
archive_id: 1,
source: 'camera',
ocr_text: capturedText,
created_at: Date.now()
}
// ③ Entity → ValuesBucket(DAO 层)
const values: relationalStore.ValuesBucket = { /* ... */ }
// ④ ValuesBucket → RDB 存储
const id = await store.insert('handwriting', values)
// ⑤ 数据库存储 → 界面呈现(翻到下一篇文章")
// store.query → ResultSet → Entity → UI
总结
本文深入解析了鸿蒙 RDB 中 ValuesBucket 的使用方法:
- 定义:
ValuesBucket = Record<string, number | string | boolean | Uint8Array | null> - 映射模式:Entity 对象 → ValuesBucket,
??操作符处理可选字段 - 字段默认值:外键未关联→
0,文本不存在→'',数值不存在→0 - 返回值:
insert返回自增主键 id,可立即用于关联表插入 - 事务优化:批量插入时使用显式事务提升性能
- 常见错误:字段名拼写、NOT NULL 约束、类型不匹配
下一篇文章将介绍 ResultSet 结果集遍历与解析——从数据库查询结果到 Entity 对象的完整转换。
如果这篇文章对你有帮助,欢迎点赞👍、收藏⭐、关注🔔,你的支持是我持续创作的动力!
参考资源:
- ValuesBucket API 参考
- RdbStore.insert 方法
- [HandwritingDao 项目源码](file:///Users/fiona/Downloads/bijixinli/harmony-app/entry/src/main/ets/features/data/HandwritingDao.ets)
- [UserDao 项目源码](file:///Users/fiona/Downloads/bijixinli/harmony-app/entry/src/main/ets/features/data/UserDao.ets)
- [ArchiveDao 项目源码](file:///Users/fiona/Downloads/bijixinli/harmony-app/entry/src/main/ets/features/data/ArchiveDao.ets)
- [ReportDao 项目源码](file:///Users/fiona/Downloads/bijixinli/harmony-app/entry/src/main/ets/features/data/ReportDao.ets)
- [RelationDao 项目源码](file:///Users/fiona/Downloads/bijixinli/harmony-app/entry/src/main/ets/features/data/RelationDao.ets)
- 鸿蒙数据持久化指南
- HarmonyOS 开发文档
七、ValuesBucket 常见问题与最佳实践
7.1 字段名大小写
ValuesBucket 的键名必须与数据库中的列名完全一致(区分大小写):
// ✅ 正确:与 CREATE TABLE 中的列名一致
values['user_id'] = data.userId
values['created_at'] = data.createdAt
// ❌ 错误:大小写不匹配
values['userId'] = data.userId // 数据库中是 user_id
values['CreatedAt'] = data.createdAt // 数据库中是 created_at
7.2 undefined vs null 的处理
ValuesBucket 不接受 undefined,可选字段必须显式设为 null:
// ✅ 正确:可选字段用 null
values['partner_id'] = data.partnerId ?? null
// ❌ 错误:undefined 会导致插入异常
values['partner_id'] = data.partnerId // 如果 partnerId 未定义会报错
7.3 数值类型选择
| ArkTS 类型 | ValuesBucket 赋值类型 | 数据库存储类型 |
|---|---|---|
number |
number |
INTEGER / REAL |
string |
string |
TEXT |
boolean |
number (0/1) |
INTEGER |
object / array |
JSON.stringify(obj) |
TEXT |
Date |
date.toISOString() |
TEXT |
7.4 批量插入优化
使用事务批量插入比逐条插入性能提升显著:
// 批量插入 —— 使用事务
async function batchCreate(records: Handwriting[]): Promise<void> {
const store = DatabaseService.getInstance().getStore()
await store.beginTransaction()
try {
for (const record of records) {
const values: relationalStore.ValuesBucket = {}
values['id'] = record.id
values['user_id'] = record.userId
values['created_at'] = record.createdAt
await store.insert('handwriting', values)
}
await store.commit()
} catch (err) {
await store.rollBack()
throw err
}
}
如果这篇文章对你有帮助,欢迎点赞👍、收藏⭐、关注🔔,你的支持是我持续创作的动力!
更多推荐


所有评论(0)