一、概述
应用数据持久化,是指应用将内存中的数据通过文件或数据库的形式保存到设备上。内存中的数据形态通常是任意的数据结构或数据对象,存储介质上的数据形态可能是文本、数据库、二进制文件等。
持久(Persistence),即把数据(如内存中的对象)保存到可永久保存的存储设备中(如磁盘)。持久化的主要应用是将内存中的数据存储在关系型的数据库中,当然也可以存储在磁盘文件中、XML数据文件中等等。
HarmonyOS标准系统支持典型的存储数据形态,包括用户首选项、键值型数据库、关系型数据库。
- 用户首选项(Preferences):通常用于保存应用的配置信息。数据通过文本的形式保存在设备中,应用使用过程中会将文本中的数据全量加载到内存中,所以访问速度快、效率高,但不适合需要存储大量数据的场景。
- 键值型数据库(KV-Store):一种非关系型数据库,其数据以“键值”对的形式进行组织、索引和存储,其中“键”作为唯一标识符。适合很少数据关系和业务关系的业务数据存储,同时因其在分布式场景中降低了解决数据库版本兼容问题的复杂度,和数据同步过程中冲突解决的复杂度而被广泛使用。相比于关系型数据库,更容易做到跨设备跨版本兼容。
- 关系型数据库(RelationalStore):一种关系型数据库,以行和列的形式存储数据,广泛用于应用中的关系型数据的处理,包括一系列的增、删、改、查等接口,开发者也可以运行自己定义的SQL语句来满足复杂业务场景的需要。
二 、用户首选项
用户首选项(Perference)为应用提供 key-value 键值型的数据处理能力,支持应用持久化轻量级数据
说白了,用户首选项就是用来保存和记录用户在操作应用的过程做做出的一些选择或设置。 可以存储一些数据,但基本都是简单类型的数据,主要作用都是存储一些用户设置数据,比如是否是首次登陆,就可以设置一个布尔类型的变量,设值为true或false。
你可以粗暴的将它看作类似于redis的以键值对存储的本地非关系型数据库(NoSql),这样方便你使用与理解。
再或者你可以将它想象成一个用来存储数据的仓库/中转站,可以跨组件的使用这些数据
约束限制
- Key键为string类型,要求非空且长度不超过80个字节。
- 如果Value值为string类型,可以为空,不为空时长度不超过8192个字节。
- 内存会随着存储数据量的增大而增大,所以存储的数据量应该是轻量级的,建议存储的数据不超过一万条,否则会在内存方面产生较大的开销。
使用步骤
1、导入用户首选项模块
import preferences from '@ohos.data.preferences'
2、获取Perferences实例,读取指定文件
preference.getPreferences(this.context,"MyAppPreferences")
.then(preferences => {
//获取成功
})
.catch(reason => {
//获取失败
})
3、 数据操作
//写入数据,如果已经存在则会覆盖,可以利用.has()判断是否存在
preferences.put('key',val)
.then(() => {
preferences.flush() //刷到磁盘中
})
.catch(reason => {}) //处理异常
//删除数据
preferences.delete('key')
.then(() => {
})
.catch(reason => {
})
//查询数据,defaultValue是默认值,没有值时就返回这个
preferences.get('key','defaultValue')
.then(value => {
})
.catch(reason => {
})
封装工具类
我们可以将这些功能封装成一个工具类
import preferences from '@ohos.data.preferences'
class PreferencesUtil {
//map集合存储多个不同的preference
preferencesMap: Map<string, preferences.Preferences> = new Map;
//加载一个preference
async loadPreference(context, name: string) {
console.log('testTag', `开始加载Preference [${name}]`);
try { //返回值是一个Promise包裹起来的preference,因此可以使用链式回调函数处理,也可以使用async/await
let preference = await preferences.getPreferences(context, name)
this.preferencesMap.set(name, preference)
console.log('testTag', `加载Preference [${name}]成功`);
return preference
} catch (err) {
console.log('testTag', `加载Preference [${name}]失败`, JSON.stringify(err));
Promise.reject(`加载Preference [${name}]失败`)
}
}
//获取指定preference并存入键值对数据
async putPreferenceValue(name: string, key: string, value: preferences.ValueType) {
if (!this.preferencesMap.has(name)) {
console.log('testTag', `Preference[${name}]尚未初始化`);
//结束异步
Promise.reject("`Preference[${name}]尚未初始化`")
}
try {
let preference = this.preferencesMap.get(name)
//写入数据
await preference.put(key, value)
//刷新磁盘
preference.flush()
console.log('testTag', `保存Preferences[${name}:${key}=${value}]成功`)
} catch (e) {
console.log('testTag', `保存Preferences[${name}.${key}=${value}]失败`, JSON.stringify(e));
}
}
//获取指定preference的指定数据
async getPreferenceValue(name: string, key: string, defaultValue: preferences.ValueType) {
if (!this.preferencesMap.has(name)) {
console.log('testTag', `Preference[${name}]尚未初始化`);
//结束异步
Promise.reject("`Preference[${name}]尚未初始化`")
}
try {
let preference = this.preferencesMap.get(name)
//读数据
let value = await preference.get(key, defaultValue)
console.log('testTag', `获取Preferences[${name}:${key}=${value}]成功`)
return value
} catch (e) {
console.log('testTag', `获取Preferences[${name}.${key}]失败`, JSON.stringify(e));
}
}
//删除指定preference的指定数据
async deletePreferenceValue(name: string, key: string, defaultValue: preferences.ValueType) {
isPreferencesHas(this.preferencesMap, name);
try {
let preference = this.preferencesMap.get(name)
//删除数据
preference.delete(key)
console.log('testTag', `删除Preferences[${name}.${key}]成功`)
return
} catch (err) {
console.log('testTag', `删除Preferences[${name}.${key}]失败`)
}
}
}
const isPreferencesHas = function (preferenceMap: Map<string, preferences.Preferences>, name) {
if (!this.preferencesMap.has(name)) {
console.log('testTag', `Preference[${name}]尚未初始化`);
//结束异步
Promise.reject("`Preference[${name}]尚未初始化`")
}
}
const preferencesUtil = new PreferencesUtil()
export default preferencesUtil as PreferencesUtil
示例
在使用preferences之前肯定要先加载一个preference实例,然后才能使用接下来的增删查功能。
既然要加载,肯定不能是像之前那样让用户点个按钮触发创建,而是最好程序已启动就自动隐式的创建,所以使用生命周期的钩子函数onCreate()方法
在EntryAbility文件中调用加载preference即可(注意这个在预览页中是无法触发该方法的,需要使用模拟器启动程序,而且此页面需要在main_pages.json中注册这个页面)
同时我们也希望当index页面一加载完毕后就获取这个刚才加载的preference,所以使用页面的钩子函数aboutToAppear()方法(相当于vue的钩子函数onMounted())
三、关系型数据库 - SQLite
关系型数据库基于SQLite组件,适用于存储包含复杂关系数据的场景,比如一个班级的学生信息,需要包括姓名、学号、各科成绩等,又或者公司的雇员信息,需要包括姓名、工号、职位等,由于数据之间有较强的对应关系,复杂程度比键值型数据更高,此时需要使用关系型数据库来持久化保存数据。
关系型数据库对应用提供通用的操作接口,底层使用SQLite作为持久化存储引擎,支持SQLite具有的数据库特性,包括但不限于事务、索引、视图、触发器、外键、参数化查询和预编译SQL语句。
鸿蒙和安卓一样支持SQLite,你或许会疑问,既然我们已经可以使用用户首选项(Preferences)做数据的持久化处理,那么为什么要使用SQLite?
使用用户首选项(SharedPreferences)是一种轻量级的本地存储方式,适用于存储小量的简单数据,例如用户设置、配置信息等。它对于快速存储和检索简单数据非常方便,但在某些情况下,你可能考虑使用 SQLite 数据库来进行数据持久化处理的原因包括:
-
复杂数据结构: 如果你的应用需要存储的数据结构较为复杂,包含多个字段或需要进行多表关联,SQLite 提供了更灵活的表结构和查询语言,使你能够更好地组织和管理数据。
-
大量数据存储: 当应用需要存储大量结构化数据时,SQLite 可以更好地处理复杂的数据存储和检索需求。SharedPreferences适用于小型数据,但对于大量的、需要进行查询和排序的数据,SQLite 提供了更强大的功能。
-
查询和过滤: 如果你需要进行复杂的数据查询、排序、过滤操作,SQLite 提供了 SQL 查询语言,可以更方便地执行这些操作。
-
支持事务: 如果你的应用需要支持事务处理,SQLite 是一个更适合的选择。事务是一种保障数据库操作原子性、一致性、隔离性和持久性的机制。
-
多用户支持: 如果你的应用需要支持多用户场景,每个用户有自己的数据,SQLite 提供了更好的多用户数据隔离能力。
就如redis和mysql之间的关系,sqlite不是来取代用户首选项的,他们是互相补充的关系,它们有各自的优势和适用场景。在许多应用中,它们并不是互相排斥的选择,而是可以根据具体需求互相补充使用。
使用步骤
1、使用关系型数据库实现数据持久化,需要获取一个RdbStore
import relationalStore from '@ohos.data.relationalStore'; // 导入模块
import UIAbility from '@ohos.app.ability.UIAbility';
class EntryAbility extends UIAbility {
onWindowStageCreate(windowStage) {
const STORE_CONFIG = {
name: 'RdbTest.db', // 数据库文件名
securityLevel: relationalStore.SecurityLevel.S1 // 数据库安全级别
};
const SQL_CREATE_TABLE = 'CREATE TABLE IF NOT EXISTS EMPLOYEE (ID INTEGER PRIMARY KEY AUTOINCREMENT, NAME TEXT NOT NULL, AGE INTEGER, SALARY REAL, CODES BLOB)'; // 建表Sql语句
relationalStore.getRdbStore(this.context, STORE_CONFIG, (err, store) => {
if (err) {
console.error(`Failed to get RdbStore. Code:${err.code}, message:${err.message}`);
return;
}
console.info(`Succeeded in getting RdbStore.`);
store.executeSql(SQL_CREATE_TABLE); // 创建数据表
// 这里执行数据库的增、删、改、查等操作
});
}
}