鸿蒙技术干货4:数据持久化方案(Preferences/RelationalStore 实战)

6 阅读6分钟

在鸿蒙应用开发中,数据持久化是实现用户状态保存、本地数据管理的核心能力。对于 3-9 个月经验的初级开发者而言,掌握轻量级的 Preferences 和关系型的 RelationalStore 是处理本地数据的基础。本文将从两种存储方案的核心特性、API 使用入手,结合「用户登录缓存 + 历史记录存储」的实战场景,详解鸿蒙本地数据持久化的实现方式,代码可直接复用。

一、轻量级存储:Preferences(键值对存储)

1. 核心概念与适用场景

Preferences 是鸿蒙提供的轻量级键值对存储工具,基于 XML 文件实现,适合存储简单配置信息(如用户登录状态、字体大小、主题设置等)。其特点为:

  • 存储数据类型有限:支持 string、number、boolean、Array等基础类型;
  • 操作简单:基于键值对读写,无需复杂的表结构设计;
  • 性能高效:适合小数据量(建议单文件不超过 100KB)的频繁读写。

2. 核心 API 使用

(1)获取 Preferences 实例

需通过dataPreferences.createPreferences方法获取,指定存储文件名(自定义)和是否加密(默认 false):

typescript

运行

import dataPreferences from '@ohos.data.preferences';

// 全局声明Preferences实例
let preferences: dataPreferences.Preferences | null = null;

// 初始化Preferences
async function initPreferences() {
  try {
    preferences = await dataPreferences.createPreferences(getContext(), 'user_info', false);
    console.log('Preferences初始化成功');
  } catch (err) {
    console.error('Preferences初始化失败:', err);
  }
}

(2)数据写入(保存登录状态)

使用put方法写入数据,flush方法将数据持久化到文件:

typescript

运行

// 保存用户登录信息
async function saveUserLoginInfo(userId: string, userName: string, isLogin: boolean) {
  if (!preferences) return;
  try {
    await preferences.put('userId', userId);
    await preferences.put('userName', userName);
    await preferences.put('isLogin', isLogin);
    await preferences.flush(); // 强制持久化
    console.log('登录信息保存成功');
  } catch (err) {
    console.error('登录信息保存失败:', err);
  }
}

(3)数据读取(获取登录状态)

使用get方法读取数据,需指定默认值(当键不存在时返回):

typescript

运行

// 获取用户登录状态
async function getUserLoginStatus() {
  if (!preferences) return { isLogin: false, userId: '', userName: '' };
  try {
    const isLogin = await preferences.get('isLogin', false);
    const userId = await preferences.get('userId', '');
    const userName = await preferences.get('userName', '');
    return { isLogin, userId, userName };
  } catch (err) {
    console.error('登录状态读取失败:', err);
    return { isLogin: false, userId: '', userName: '' };
  }
}

(4)数据删除(退出登录)

使用delete方法删除指定键,或clear方法清空所有数据:

typescript

运行

// 退出登录,清除登录信息
async function clearUserLoginInfo() {
  if (!preferences) return;
  try {
    await preferences.delete('userId');
    await preferences.delete('userName');
    await preferences.put('isLogin', false);
    await preferences.flush();
    console.log('登录信息已清除');
  } catch (err) {
    console.error('登录信息清除失败:', err);
  }
}

二、关系型存储:RelationalStore(结构化数据存储)

1. 核心概念与适用场景

RelationalStore 是鸿蒙基于 SQLite 实现的关系型数据库,支持结构化数据的存储与复杂查询,适合存储有关联的批量数据(如用户操作历史、商品列表、聊天记录等)。其特点为:

  • 支持 SQL 语法:可通过 SQL 语句或 ORM 方式操作数据;
  • 表结构化:需预先定义表结构,支持主键、索引、约束等;
  • 事务支持:可处理多表关联、批量数据操作的原子性。

2. 表结构设计与数据库初始化

(1)定义历史记录表结构

以「用户操作历史记录」为例,设计表结构如下:

字段名类型说明
idINTEGER主键(自增)
userIdTEXT关联用户 ID
actionTEXT操作行为(如 “登录”“查看详情”)
timeINTEGER操作时间(时间戳)

(2)初始化 RelationalStore 数据库

需创建数据库配置、表结构,并获取数据库实例:

typescript

运行

import relationalStore from '@ohos.data.relationalStore';

// 数据库名称
const DB_NAME = 'user_history.db';
// 数据库版本号
const DB_VERSION = 1;
// 表名
const TABLE_NAME = 'history';

// 创建表的SQL语句
const CREATE_TABLE_SQL = `
  CREATE TABLE IF NOT EXISTS ${TABLE_NAME} (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    userId TEXT NOT NULL,
    action TEXT NOT NULL,
    time INTEGER NOT NULL
  );
`;

// 获取数据库实例
async function getRdbStore() {
  const config = {
    name: DB_NAME,
    version: DB_VERSION,
    encrypt: false,
    createTables: CREATE_TABLE_SQL
  };
  try {
    const rdbStore = await relationalStore.getRdbStore(getContext(), config);
    console.log('RelationalStore初始化成功');
    return rdbStore;
  } catch (err) {
    console.error('RelationalStore初始化失败:', err);
    return null;
  }
}

3. CRUD 操作(历史记录管理)

(1)插入数据(添加操作历史)

typescript

运行

// 添加操作历史
async function addHistoryRecord(userId: string, action: string) {
  const rdbStore = await getRdbStore();
  if (!rdbStore) return;
  try {
    const values = new relationalStore.ValuesBucket();
    values.putString('userId', userId);
    values.putString('action', action);
    values.putLong('time', Date.now());
    // 插入数据
    const rowId = await rdbStore.insert(TABLE_NAME, values);
    console.log('历史记录添加成功,行ID:', rowId);
  } catch (err) {
    console.error('历史记录添加失败:', err);
  }
}

(2)查询数据(获取用户操作历史)

typescript

运行

// 获取指定用户的操作历史
async function getHistoryRecords(userId: string) {
  const rdbStore = await getRdbStore();
  if (!rdbStore) return [];
  try {
    // 构建查询条件
    const predicates = new relationalStore.RdbPredicates(TABLE_NAME);
    predicates.equalTo('userId', userId);
    predicates.orderByDesc('time'); // 按时间倒序
    // 执行查询
    const records = await rdbStore.query(predicates, ['id', 'action', 'time']);
    // 解析查询结果
    const historyList: Array<{ id: number; action: string; time: number }> = [];
    while (records.goToNextRow()) {
      historyList.push({
        id: records.getLong(0),
        action: records.getString(1),
        time: records.getLong(2)
      });
    }
    records.close(); // 关闭结果集
    console.log('历史记录查询成功,共', historyList.length, '条');
    return historyList;
  } catch (err) {
    console.error('历史记录查询失败:', err);
    return [];
  }
}

(3)删除数据(清空指定用户历史)

typescript

运行

// 清空指定用户的操作历史
async function clearHistoryRecords(userId: string) {
  const rdbStore = await getRdbStore();
  if (!rdbStore) return;
  try {
    const predicates = new relationalStore.RdbPredicates(TABLE_NAME);
    predicates.equalTo('userId', userId);
    const count = await rdbStore.delete(predicates);
    console.log('历史记录删除成功,共删除', count, '条');
  } catch (err) {
    console.error('历史记录删除失败:', err);
  }
}

三、实战整合:用户登录缓存 + 历史记录存储

1. 业务流程梳理

  1. 应用启动时,初始化 Preferences 和 RelationalStore;
  2. 用户登录成功后,通过 Preferences 保存登录状态,通过 RelationalStore 添加 “登录” 操作历史;
  3. 应用首页读取 Preferences 的登录状态,未登录则跳转登录页,已登录则查询 RelationalStore 的操作历史并展示;
  4. 用户退出登录时,清除 Preferences 的登录信息,可选择是否保留历史记录。

2. 核心整合代码示例

typescript

运行

// 应用启动初始化
async function appInit() {
  await initPreferences(); // 初始化Preferences
  await getRdbStore(); // 初始化RelationalStore
  // 检查登录状态
  const { isLogin, userId, userName } = await getUserLoginStatus();
  if (isLogin) {
    console.log('用户已登录:', userName);
    // 添加登录历史
    await addHistoryRecord(userId, '登录应用');
    // 查询并展示历史记录
    const historyList = await getHistoryRecords(userId);
    console.log('用户操作历史:', historyList);
  } else {
    console.log('用户未登录,跳转登录页');
  }
}

// 模拟用户登录操作
async function userLogin(userId: string, userName: string) {
  await saveUserLoginInfo(userId, userName, true);
  await addHistoryRecord(userId, '登录应用');
}

// 模拟用户退出登录操作
async function userLogout(userId: string) {
  await clearUserLoginInfo();
  await addHistoryRecord(userId, '退出登录');
}

四、常见问题与解决方案

1. Preferences 数据未持久化

  • 需调用flush方法强制将内存数据写入文件(默认情况下,鸿蒙会在合适时机自动持久化,但主动调用更可靠);
  • 检查存储文件名是否包含特殊字符(建议仅使用字母、数字、下划线)。

2. RelationalStore 表创建失败

  • 检查 SQL 语句语法是否正确(如字段类型、括号匹配、分号结尾);
  • 数据库版本号更新后,需处理表结构升级(可在onUpgrade回调中实现)。

3. 数据读取权限问题

  • 确保在config.json中声明ohos.permission.WRITE_USER_DATAohos.permission.READ_USER_DATA权限:

json

{
  "module": {
    "reqPermissions": [
      {
        "name": "ohos.permission.WRITE_USER_DATA"
      },
      {
        "name": "ohos.permission.READ_USER_DATA"
      }
    ]
  }
}

五、进阶学习方向

  1. 数据加密:Preferences 和 RelationalStore 均支持加密存储,可通过encrypt参数开启,结合鸿蒙的安全模块实现敏感数据加密;
  2. 数据库迁移:处理 RelationalStore 版本升级时的表结构变更、数据迁移逻辑;
  3. 异步操作优化:使用 Promise、async/await 优化数据操作的异步流程,避免 UI 线程阻塞;
  4. 分布式数据存储:学习鸿蒙的 DistributedDataAbility,实现多设备间的数据同步。

加入班级,一起学习鸿蒙开发