鸿蒙学习实战之路-数据持久化键值型数据库KV-Store全攻略

最近好多朋友问我:“西兰花啊,鸿蒙应用里除了Preferences,还有什么靠谱的存储方式吗?我要存的东西有点多,但又不想用复杂的关系型数据库,咋整?” 害,这问题可问对人了!

今天这篇,我就手把手带你搞定鸿蒙里的键值型数据库(KV-Store),从基础概念到实战代码,全程用大白话讲明白~

一、什么是应用数据持久化?

应用数据持久化是指应用将内存中的数据通过文件或数据库的形式保存到设备上。内存中的数据形态通常是任意的数据结构或数据对象,存储介质上的数据形态可能是文本、数据库、二进制文件等。

HarmonyOS标准系统支持典型的存储数据形态,包括:

  • 用户首选项(Preferences):用于保存应用的配置信息。数据以文本形式保存在设备中,应用使用时会将文本中的数据全量加载到内存中,访问速度快、效率高,但不适合需要存储大量数据的场景。

  • 键值型数据库(KV-Store):一种非关系型数据库,其数据以"键值"对的形式进行组织、索引和存储,其中"键"作为唯一标识符。适合数据关系和业务关系较少的业务数据存储,同时因其在分布式场景中降低了解决数据库版本兼容问题的复杂度和数据同步过程中冲突解决的复杂度而被广泛使用。相比于关系型数据库,更容易做到跨设备跨版本兼容。

  • 关系型数据库(RelationalStore):一种关系型数据库,以行和列的形式存储数据,广泛用于关系型数据处理,支持增、删、改、查等接口,开发者也可以运行自定义SQL语句满足复杂业务场景。此外,提供了向量数据库能力,支持向量数据间的相似度计算,适用于推荐场景、相似图像检索以及自然语言处理等。

今天咱们重点聊聊键值型数据库(KV-Store),这玩意儿就像咱们家里的储物盒,每个盒子都有个标签(键),里面装着对应的东西(值),找起来方便又高效!

二、KV-Store适合存什么?

KV-Store特别适合存储那些没有复杂关系的数据,比如:

  • 商品名称及对应价格
  • 员工工号及出勤状态
  • 简单结构的配置信息
  • 用户的收藏列表
  • 其他键值对应的数据

就像咱们厨房里的调料瓶,每个瓶子(键)都装着不同的调料(值),一目了然,拿取方便!

三、注意事项要牢记

使用KV-Store时,有一些"家规"得遵守,不然容易出问题:

限制项 具体说明
设备协同数据库 Key长度≤896 Byte,Value长度<4 MB
单版本数据库 Key长度≤1 KB,Value长度<4 MB
并发限制 每个应用最多同时打开16个键值型分布式数据库
事件回调 不允许进行阻塞操作(如修改UI组件)

🥦 西兰花警告
别存太大的东西!每个值的大小不能超过4MB,就像储物盒不能装超过它容量的东西一样~

四、核心接口速览

KV-Store提供了一套简单好用的接口,咱们来快速认识一下:

接口名称 描述
createKVManager 创建KVManager实例,用来管理数据库对象(就像创建储物间管理员)
getKVStore 创建并获取指定类型的KVStore数据库(就像在储物间里开辟一个新的储物区)
put 添加键值对到数据库(就像把东西放进标好标签的盒子里)
get 获取指定键的值(就像根据标签找到对应的盒子并取出东西)
delete 删除指定键值的数据(就像扔掉不需要的东西)
closeKVStore 关闭指定数据库(就像用完储物区后锁上门)
deleteKVStore 删除指定数据库(就像拆掉整个储物区)

所有异步接口都支持callback和Promise两种形式,咱们可以根据自己的习惯选择~

五、实战代码走起来

光说不练假把式,咱们直接上代码!

1. 创建KVManager实例

首先得创建一个KVManager实例,它就像是咱们的储物间管理员,负责管理所有的KVStore数据库。

Stage模型:

import { distributedKVStore } from "@kit.ArkData";
import { UIAbility } from "@kit.AbilityKit";
import { BusinessError } from "@kit.BasicServicesKit";

// 定义KVManager实例和相关配置
let kvManager: distributedKVStore.KVManager | undefined = undefined;
let appId: string = "com.example.datamanagertest"; // 你的应用ID
let storeId: string = "myKVStore"; // 数据库ID

export default class EntryAbility extends UIAbility {
  onCreate() {
    // 配置KVManager
    const kvManagerConfig: distributedKVStore.KVManagerConfig = {
      context: this.context, // 应用上下文
      bundleName: appId, // 应用包名
    };
    
    try {
      // 创建KVManager实例
      kvManager = distributedKVStore.createKVManager(kvManagerConfig);
      console.info("成功创建KVManager实例");
    } catch (e) {
      let error = e as BusinessError;
      console.error(`创建KVManager失败: ${error.code}, ${error.message}`);
    }
  }
}

FA模型:

import { distributedKVStore } from "@kit.ArkData";
import { featureAbility } from "@kit.AbilityKit";
import { BusinessError } from "@kit.BasicServicesKit";

// 定义KVManager实例和相关配置
let kvManager: distributedKVStore.KVManager | undefined = undefined;
let appId: string = "com.example.datamanagertest"; // 你的应用ID
let storeId: string = "myKVStore"; // 数据库ID
let context = featureAbility.getContext(); // 获取上下文

// 配置KVManager
const kvManagerConfig: distributedKVStore.KVManagerConfig = {
  context: context,
  bundleName: appId,
};

try {
  // 创建KVManager实例
  kvManager = distributedKVStore.createKVManager(kvManagerConfig);
  console.info("成功创建KVManager实例");
} catch (e) {
  let error = e as BusinessError;
  console.error(`创建KVManager失败: ${error.code}, ${error.message}`);
}

2. 获取KVStore数据库

有了管理员,接下来咱们需要创建一个具体的KVStore数据库,就像在储物间里开辟一个专门的储物区。

let kvStore: distributedKVStore.SingleKVStore | undefined = undefined;

try {
  // 创建Schema(可选,用于定义数据结构)
  // 定义id字段
  let child1 = new distributedKVStore.FieldNode("id");
  child1.type = distributedKVStore.ValueType.INTEGER; // 整数类型
  child1.nullable = false; // 不能为空
  child1.default = "1"; // 默认值

  // 定义name字段
  let child2 = new distributedKVStore.FieldNode("name");
  child2.type = distributedKVStore.ValueType.STRING; // 字符串类型
  child2.nullable = false; // 不能为空
  child2.default = "zhangsan"; // 默认值

  // 创建Schema实例
  let schema = new distributedKVStore.Schema();
  schema.root.appendChild(child1); // 添加id字段
  schema.root.appendChild(child2); // 添加name字段
  schema.indexes = ["$.id", "$.name"]; // 创建索引,提高查询效率
  schema.mode = 1; // 1表示STRICT模式,0表示COMPATIBLE模式
  schema.skip = 0;

  // 配置KVStore
  const options: distributedKVStore.Options = {
    createIfMissing: true, // 如果不存在则创建
    encrypt: false, // 不加密
    backup: false, // 不备份
    autoSync: false, // 不自动同步
    kvStoreType: distributedKVStore.KVStoreType.SINGLE_VERSION, // 单版本数据库
    // kvStoreType: distributedKVStore.KVStoreType.DEVICE_COLLABORATION, // 多设备协同数据库
    schema: schema, // 使用上面定义的Schema
    securityLevel: distributedKVStore.SecurityLevel.S3, // 安全级别
  };

  // 获取KVStore实例
  kvManager.getKVStore<distributedKVStore.SingleKVStore>(
    storeId,
    options,
    (err, store) => {
      if (err) {
        console.error(`获取KVStore失败: ${err.code}, ${err.message}`);
        return;
      }
      console.info("成功获取KVStore实例");
      kvStore = store;
    }
  );
} catch (e) {
  let error = e as BusinessError;
  console.error(`发生意外错误: ${error.code}, ${error.message}`);
}

🥦 西兰花小贴士
Schema是可选的,但使用它可以让你的数据结构更规范,查询效率更高哦~

3. 订阅数据变化

想实时知道数据库里的数据有没有变化?可以订阅数据变更事件:

try {
  kvStore.on(
    "dataChange",
    distributedKVStore.SubscribeType.SUBSCRIBE_TYPE_ALL,
    (data) => {
      console.info(`数据发生变化: ${data}`);
      // 注意:这里不能做阻塞操作,比如修改UI组件
    }
  );
} catch (e) {
  let error = e as BusinessError;
  console.error(`订阅数据变化失败: ${error.code}, ${error.message}`);
}

4. 插入数据

现在可以往KVStore里存数据了,就像把东西放进标好标签的盒子里:

// 定义测试数据
const KEY_TEST_STRING_ELEMENT = "key_test_string";
const VALUE_TEST_STRING_ELEMENT = '{"id":0, "name":"lisi"}';

try {
  // 插入数据
  kvStore.put(KEY_TEST_STRING_ELEMENT, VALUE_TEST_STRING_ELEMENT, (err) => {
    if (err !== undefined) {
      console.error(`插入数据失败: ${err.code}, ${err.message}`);
      return;
    }
    console.info("成功插入数据");
  });
} catch (e) {
  let error = e as BusinessError;
  console.error(`发生意外错误: ${error.code}, ${error.message}`);
}

5. 查询数据

存进去的数据得能读出来才行,就像根据标签找到对应的盒子并取出东西:

try {
  // 查询数据
  kvStore.get(KEY_TEST_STRING_ELEMENT, (err, data) => {
    if (err != undefined) {
      console.error(`查询数据失败: ${err.code}, ${err.message}`);
      return;
    }
    console.info(`成功查询数据: ${data}`);
  });
} catch (e) {
  let error = e as BusinessError;
  console.error(`查询数据失败: ${error.code}, ${error.message}`);
}

6. 删除数据

不需要的数据可以删掉,就像扔掉不需要的东西:

try {
  // 删除数据
  kvStore.delete(KEY_TEST_STRING_ELEMENT, (err) => {
    if (err !== undefined) {
      console.error(`删除数据失败: ${err.code}, ${err.message}`);
      return;
    }
    console.info("成功删除数据");
  });
} catch (e) {
  let error = e as BusinessError;
  console.error(`发生意外错误: ${error.code}, ${error.message}`);
}

7. 关闭数据库

用完数据库后记得关闭它,就像用完储物区后锁上门:

try {
  // 关闭数据库
  kvManager.closeKVStore(appId, storeId, (err: BusinessError) => {
    if (err) {
      console.error(`关闭数据库失败: ${err.code}, ${err.message}`);
      return;
    }
    console.info("成功关闭数据库");
    kvStore = undefined; // 清空实例引用
  });
} catch (e) {
  let error = e as BusinessError;
  console.error(`发生意外错误: ${error.code}, ${error.message}`);
}

8. 删除数据库

如果不想用某个数据库了,可以删掉它,就像拆掉整个储物区:

try {
  // 删除数据库
  kvManager.deleteKVStore(appId, storeId, (err: BusinessError) => {
    if (err) {
      console.error(`删除数据库失败: ${err.code}, ${err.message}`);
      return;
    }
    console.info("成功删除数据库");
    kvStore = undefined; // 清空实例引用
  });
} catch (e) {
  let error = e as BusinessError;
  console.error(`发生意外错误: ${error.code}, ${error.message}`);
}

六、总结一下

今天咱们一起学习了鸿蒙里的键值型数据库(KV-Store),从基础概念到实战代码,相信你已经掌握了:

  1. KV-Store的适用场景:适合存储无复杂关系模型的数据
  2. KV-Store的约束限制:键值大小、并发数量等
  3. KV-Store的核心接口:创建、获取、增删改查等操作
  4. 实战代码:从创建实例到数据操作的完整流程

🥦 西兰花小贴士
如果你的数据量不大,结构简单,KV-Store是个不错的选择!如果需要处理复杂的关系型数据,那还是得用关系型数据库(RelationalStore)~

👉 预告:《只会KV-Store?HarmonyOS关系型数据库RelationalStore全攻略》

📚 推荐资料:

我是盐焗西兰花,
不教理论,只给你能跑的代码和避坑指南。
下期见!🥦

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐