鸿蒙ArkData键值型数据库实战:Schema 定义与商品库存同步案例
鸿蒙ArkData键值型数据库实战:Schema 定义与商品库存同步案例
键值型数据库(KV-Store)适合数据关系简单、又可能要跨设备同步的场景。下面用一个"商品库存管理"的完整案例,把 KVManager 创建、Schema 定义、加密、读写、订阅、关闭销毁走一遍,重点讲清楚 Schema 这个容易忽略但很关键的概念。
一、案例背景:多门店商品库存
假设一个零售应用,要管理多个门店的商品库存。数据结构很简单:商品 ID 映射到库存信息(名称、数量、是否上架)。这类数据:
- 关系简单(就是商品 ID → 库存对象)
- 可能要跨设备同步(手机端改了库存,平板端要看到)
- 需要加密(库存数据敏感)
正好是 KV-Store 的主场。
二、第一步:创建 KVManager
KVManager 是管理数据库对象的入口,整个应用创建一次即可。
import { distributedKVStore } from '@kit.ArkData';
import { BusinessError } from '@kit.BasicServicesKit';
let kvManager: distributedKVStore.KVManager | undefined = undefined;
let kvStore: distributedKVStore.SingleKVStore | undefined = undefined;
const appId = 'com.example.retail';
const storeId = 'inventory_store';
const context = EntryAbility.getContext();
$0; // 占位,实际从 EntryAbility 获取
function createKvManager() {
if (kvManager !== undefined) {
console.info('KVManager 已创建');
return;
}
const kvManagerConfig: distributedKVStore.KVManagerConfig = {
bundleName: appId,
context: context
};
try {
kvManager = distributedKVStore.createKVManager(kvManagerConfig);
console.info('KVManager 创建成功');
} catch (err) {
console.error(`创建失败: ${(err as BusinessError).message}`);
}
}
三、第二步:定义 Schema(关键且常被忽略)
Schema 是 KV-Store 里一个容易被忽略的概念。它定义了 Value 的字段结构、类型、索引。定义 Schema 的好处:
- 数据有约束,写入不合规数据会被拦截
- 建了索引,查询更快
- 分布式同步时,两端 Schema 一致才能正确同步
function getKvStore() {
if (kvManager === undefined) {
console.info('KVManager 未初始化');
return;
}
try {
// 定义字段节点
let idField = new distributedKVStore.FieldNode('id');
idField.type = distributedKVStore.ValueType.INTEGER;
idField.nullable = false;
idField.default = '0';
let nameField = new distributedKVStore.FieldNode('name');
nameField.type = distributedKVStore.ValueType.STRING;
nameField.nullable = false;
nameField.default = '';
let stockField = new distributedKVStore.FieldNode('stock');
stockField.type = distributedKVStore.ValueType.INTEGER;
stockField.nullable = false;
stockField.default = '0';
// 组装 Schema
let schema = new distributedKVStore.Schema();
schema.root.appendChild(idField);
schema.root.appendChild(nameField);
schema.root.appendChild(stockField);
schema.indexes = ['$.id', '$.name']; // 索引路径
schema.mode = 1; // 1 表示 STRICT 严格模式,0 表示 COMPATIBLE
schema.skip = 0;
const options: distributedKVStore.Options = {
createIfMissing: true,
encrypt: true, // 开启加密
backup: false,
autoSync: false,
kvStoreType: distributedKVStore.KVStoreType.SINGLE_VERSION, // 单版本数据库
schema: schema,
securityLevel: distributedKVStore.SecurityLevel.S3 // 安全级别 S3
};
kvManager.getKVStore<distributedKVStore.SingleKVStore>(
storeId, options,
(err, store: distributedKVStore.SingleKVStore) => {
if (err) {
console.error(`获取 KVStore 失败: ${err.message}`);
return;
}
kvStore = store;
console.info('KVStore 获取成功');
}
);
} catch (e) {
console.error(`异常: ${(e as BusinessError).message}`);
}
}
几个配置项要理解清楚:
kvStoreType:SINGLE_VERSION单版本数据库;DEVICE_COLLABORATION多设备协同数据库。不填默认多设备协同。encrypt: true:开启加密,库存这种敏感数据必开securityLevel: S3:安全级别,S1-S4,S3 适合一般敏感业务数据schema.mode = 1:STRICT 严格模式,写入会校验字段类型;COMPATIBLE 模式更宽松
四、第三步:订阅数据变化
库存变化要实时通知 UI 刷新,用订阅:
function subscribeDataChange() {
if (kvStore === undefined) {
return;
}
try {
kvStore.on('dataChange', distributedKVStore.SubscribeType.SUBSCRIBE_TYPE_ALL, (data) => {
console.info(`数据变化: ${JSON.stringify(data)}`);
// 这里可以通知 UI 刷新库存列表
});
} catch (e) {
console.error(`订阅失败: ${(e as BusinessError).message}`);
}
}
SUBSCRIBE_TYPE_ALL 表示订阅所有类型的变化。注意官方提醒:回调方法里不允许做阻塞操作,比如修改 UI 组件。要刷 UI 的话,通过 AppStorage 或 emitter 把数据抛出去,在 UI 线程处理。
五、第四步:写入库存数据
function putInventory() {
if (kvStore === undefined) {
return;
}
const key = 'product_1001';
// Value 必须符合 Schema 定义的字段结构
const value = '{"id":1001, "name":"无线耳机", "stock":128}';
try {
kvStore.put(key, value, (err) => {
if (err !== undefined) {
console.error(`写入失败: ${err.message}`);
return;
}
console.info('库存写入成功');
});
} catch (e) {
console.error(`异常: ${(e as BusinessError).message}`);
}
}
关键点:当 Key 已存在时,put 会覆盖原值;不存在则新增。Value 是 JSON 字符串,字段要和 Schema 对应——STRICT 模式下,字段类型不匹配会被拒绝。
六、第五步:读取和删除
// 读取
function getInventory() {
if (kvStore === undefined) return;
const key = 'product_1001';
kvStore.get(key, (err, data) => {
if (err != undefined) {
console.error(`读取失败: ${err.message}`);
return;
}
console.info(`读取结果: ${data}`);
// data 是 '{"id":1001, "name":"无线耳机", "stock":128}'
let inventory = JSON.parse(data as string);
});
}
// 删除2
function deleteInventory() {
if (kvStore === undefined) return;
const key = 'product_1001';
kvStore.delete(key, (err) => {
if (err !== undefined) {
console.error(`删除失败: ${err.message}`);
return;
}
console.info('删除成功');
});
}
七、第六步:关闭和删除数据库
// 关闭数据库(释放资源,数据保留)
function closeKvStore() {
if (kvManager === undefined) return;
kvStore = undefined;
kvManager.closeKVStore(appId, storeId, (err: BusinessError) => {
if (err) {
console.error(`关闭失败: ${err.message}`);
return;
}
console.info('关闭成功');
});
}
// 删除数据库(数据也删除)
function deleteKvStore() {
if (kvManager === undefined) return;
kvStore = undefined;
kvManager.deleteKVStore(appId, storeId, (err: BusinessError) => {
if (err) {
console.error(`删除失败: ${err.message}`);
return;
}
console.info('删除成功');
});
}
八、约束限制速查
| 约束 | 说明 |
|---|---|
| Key 长度 | 单版本 ≤ 1KB;设备协同 ≤ 896 Byte |
| Value 长度 | < 4MB |
| 同时打开数据库数 | 每应用最多 16 个 |
| 回调里阻塞操作 | 不允许(如改 UI) |
九、单版本 vs 多设备协同:怎么选
KV-Store 有两种数据库类型,选哪个看同步需求:
- SINGLE_VERSION(单版本):本地数据库,不自动跨设备同步。适合纯本地存储,或手动控制同步时机。
- DEVICE_COLLABORATION(多设备协同):支持跨设备自动同步。适合需要多端实时一致的场景。
本案例如果只是单门店本地管理,用 SINGLE_VERSION 就够。如果要多门店跨设备同步库存,用 DEVICE_COLLAB)LABORATION,并配合分布式同步接口(下一篇关系型会详细讲,KV 的同步思路类似)。
十、实战封装:InventoryRepository
把散装 API 封装成仓储类,业务侧更干净:
export class InventoryRepository {
private store: distributedKVStore.SingleKVStore | undefined;
constructor(store: distributedKVStore.SingleKVStore) {
this.store = store;
}
async save(productId: number, name: string, stock: number): Promise<void> {
return new Promise((resolve, reject) => {
if (!this.store) { reject(new Error('store 未初始化')); return; }
const key = `product_${productId}`;
const value = JSON.stringify({ id: productId, name, stock });
this.store.put(key, value, (err) => {
if (err) reject(err);
else resolve();
});
});
}
async get(productId: number): Promise<{id: number, name: string, stock: number} | null> {
return new Promise((resolve, reject) => {
if (!this.store) { reject(new Error('store 未初始化')); return; }
$ this.store.get(`product_${productId}`, (err, data) => {
if (err) reject(err);
else if (data) resolve(JSON.parse(data as string));
else resolve(null);
});
});
}
}
十一、几条经验哦
- Schema 不是可选项:定义字段、类型、索引,既能约束数据,又能加速查询和同步
- 敏感数据开 encrypt:KV-Store 原生支持加密,不用自己加解密
- 回调里别做阻塞操作:要刷 UI 用 AppStorage/emitter 抛出去
- 选对数据库类型:本地用 SINGLE_VERSION,跨设备用 DEVICE_COLLABORATION
- Value 是 JSON 字符串:字段要和 Schema 对应,STRICT 模式会校验
下一篇进入关系型数据库,处理更复杂的数据关系和 SQL 场景。
更多推荐

所有评论(0)