1. 介绍
数据存储属于ArkData(方舟数据管理)的一部分。ArkData除了提供数据存储的能力,也提供了数据管理和数据同步能力,比如联系人应用数据可以保存到数据库中,提供数据库的安全、可靠以及共享访问等管理机制,也支持与手表同步联系人信息。
我们这里先了解下数据存储,应用创建的数据库,存储的数据都是会保存到应用沙盒中,当应用卸载时,数据库也会自动删除。
在ArkData中,根据数据特点,数据持久化存储的方式有以下三种:
-
用户首选项(Preferences):
- 类似iOS的UserDefaults或者Android的SharedPreference,提供了轻量级配置数据的持久能力;
- 支持订阅数据变化的通知能力;
- 不支持分布式同步,常用于保存应用配置信息、用户偏好设置等;
- 会将文本中的数据全量加载到内存中,访问快,效率高,但不适合存储大量数据;
-
键值型数据库(KV-Store):
- 提供了键值型数据库的读写、加密、手动备份以及订阅通知的能力;
- 应用需要使用键值型数据库的分布式能力时,KV-Store会将同步请求发送给DatamgrService由其完成跨设备数据同步;
-
关系型数据库(RelationStore):
- 提供了关系型数据库的读写、加密、手动备份以及订阅通知的能力;
- 如果需要使用关系型数据库的分布式能力时,RelationalStore部件会将同步请求发送给DatamgrService由其完成跨设备数据同步
- 比如如果要在App内部存储复杂的数据结构,可以使用关系型数据库;
2.用户首选项
2.1 场景介绍
用户首选项为应用提供Key-Value键值型的数据处理能力,支持应用持久化轻量级数据,并对其修改和查询。其会将数据缓存放在内存中,当用户读取的时候,能够从内存中快速获取数据,当需要持久化的时候可以使用flush接口将内存中的数据写入持久化文件中。
为了提升数据读写效率,用户首选项会将所有数据存到内存,随着数据量越多,会导致应用占用的内存越大,因此不适合存放过多的数据,适用的场景一般为应用保存用户的个性化设置等。
2.2 运作机制
从下往上看,用户首选项也是以文件的形式存储到应用沙箱内的,开发者可以将用户首选项持久化文件的内容加载到Preferences实例(一个文件对应一个Preferences实例),系统会通过静态容器将该实例存储在内存中,直到主动从内存中移除该实例或者删除该文件。
对于Key和Value有一些限制:
- Key为string类型,非空且长度<=80个字节;
- Value为string时,长度<=8192个字节;
- 建议存储的数据不超过1万条;
2.3 接口说明
用户首选项和iOS的UserDefault,Android的SharedPreference的使用方法很像,都有基本的增、删、改、查方法,但不同的是用户首选项提供了订阅数据变更的能力:
// 获取Preferences实例。该接口存在异步接口。
getPreferencesSync(context: Context, options: Options): Preferences
// 将数据写入Preferences实例,可通过flush将Preferences实例持久化。该接口存在异步接口。
putSync(key: string, value: ValueType): void
// 检查Preferences实例是否包含名为给定Key的存储键值对。给定的Key值不能为空。该接口存在异步接口。
hasSync(key: string): void
// 获取键对应的值,如果值为null或者非默认值类型,返回默认数据defValue。该接口存在异步接口。
getSync(key: string, defValue: ValueType): void
// 从Preferences实例中删除名为给定Key的存储键值对。该接口存在异步接口。
deleteSync(key: string): void
// 将当前Preferences实例的数据异步存储到用户首选项持久化文件中。
flush(callback: AsyncCallback<void>): void
// 订阅数据变更,订阅的数据发生变更后,在执行flush方法后,触发callback回调。
on(type: 'change', callback: Callback<string>): void
// 取消订阅数据变更。
off(type: 'change', callback?: Callback<string>): void
// 从内存中移除指定的Preferences实例。若Preferences实例有对应的持久化文件,则同时删除其持久化文件。
deletePreferences(context: Context, options: Options, callback: AsyncCallback<void>): void
2.4 举例说明
// 导入包
import dataPreferences from '@ohos.data.preferences';
// 获取context
let context = getContext(this);
export default class PreferencesManager {
static shared = new PreferencesManager();
preferences?: dataPreferences.Preferences;
preferencesName: string = 'CommonPreferences';
// 初始化preferences实例
initPreferences() {
this.preferences = dataPreferences.getPreferencesSync(context, { name: this.preferencesName });
}
// 设置数据
set(key: string, value: dataPreferences.ValueType) {
if (!this.preferences) {
this.initPreferences();
}
this.preferences?.putSync(key, value);
this.preferences?.flush();
}
// 获取数据
get(key: string): dataPreferences.ValueType | null | undefined {
if (!this.preferences) {
this.initPreferences();
}
let value = this.preferences?.getSync(key, null);;
return value;
}
// 删除数据
delete(key: string) {
if (!this.preferences) {
this.initPreferences();
}
if (this.preferences?.hasSync(key)) {
this.preferences.deleteSync(key);
this.preferences.flush();
}
}
}
3. 键值型数据库
3.1 介绍
键值型数据库用于存储键值对形式的数据,适用于简单的存储场景:比如存储商品名称及对应价格、员工工号及今日是否已出勤等。
- 针对于单版本数据库,针对每条记录,Key的长度<=1KB,Value的长度<4MB;
- 设备协同数据库,针对每条记录,Key的长度<=896 Byte, Value的长度<4MB;
- 每个应用程序最多支持同时打开16个键值型分布式数据库;
- 键值型数据库事件回调方法中不允许进行阻塞操作,例如修改UI组件;
3.2 接口说明
键值型数据库相关的接口,大部分为异步接口,均有callback和Promise两种返回形式。
// 创建一个KVManager对象实例,用于管理数据库对象
createKVManager(config: KVManagerConfig): KVManager
// 指定Options和storeId,创建并得到指定类型的KVStore数据库
getKVStore<T>(storeId: string, options: Options, callback: AsyncCallback<T>): void
// 添加指定类型的键值对到数据库
put(key: string, value: Uint8Array|string|number|boolean, callback: AsyncCallback<void>): void
// 获取指定键的值
get(key: string, callback: AsyncCallback<Uint8Array|string|boolean|number>): void
// 从数据库中删除指定键值的数据
delete(key: string, callback: AsyncCallback<void>): void
3.3 举例说明
// 导入模块
import distributedKVStore from '@ohos.data.distributedKVStore';
// 获取context
let context = getContext(this);
export default class KVStoreManager {
// 单例模式
static shared = new KVStoreManager();
// SingleKVStore实例
KVStore?: distributedKVStore.SingleKVStore;
// 创建并获取KVStore
async initKVStore() {
try {
// Create KVManager
let bundleName = await getBundleName();
const kvManagerConfig: distributedKVStore.KVManagerConfig = {
context: context,
bundleName: bundleName
};
const manager = distributedKVStore.createKVManager(kvManagerConfig);
// Create KVStore
const options: distributedKVStore.Options = {
createIfMissing: true,
encrypt: false,
backup: false,
autoSync: false,
kvStoreType: distributedKVStore.KVStoreType.SINGLE_VERSION,
securityLevel: distributedKVStore.SecurityLevel.S1
};
this.KVStore = await manager.getKVStore<distributedKVStore.SingleKVStore>('storeId', options);
} catch (error) {
console.log('[KVStoreManager]', `Failed to initKVStore, Cause: ${error}`);
}
}
// 插入数据
async set(key: string, value: Uint8Array | string | number | boolean) {
if (!this.KVStore) {
await this.initKVStore();
}
try {
await this.KVStore?.put(key, value);
} catch (error) {
console.log('[KVStoreManager]', `Failed to set value, Cause: ${error}`);
}
}
// 获取数据
async get(key: string): Promise<string | number | boolean | Uint8Array | null | undefined> {
if (!this.KVStore) {
await this.initKVStore();
}
let value: Uint8Array | string | number | boolean | null | undefined = null;
try {
return this.KVStore?.get(key);
} catch (error) {
console.log('[KVStoreManager]', `Failed to get value, Cause: ${error}`);
}
return value;
}
// 删除数据
async delete(key: string) {
if (!this.KVStore) {
await this.initKVStore();
}
try {
await this.KVStore?.delete(key);
} catch (error) {
console.log('[KVStoreManager]', `Failed to delete value, Cause: ${error}`);
}
}
}
4. 关系型数据库
4.1 介绍
关系型数据库基于SQLite组件,适用于包含复杂关系数据的场景。比如购物车的商品数据,需要包括商品名称、价格、库存等。
基本概念:
- 谓词:用来定义数据库的操作条件;
- 结果集:执行查询之后的结果集合;
运作机制: 关系型数据库对应用提供通用的操作接口,底层使用SQLite作为持久化存储引擎,支持SQLite具有的数据库特性。
- 数据库同一时间只能支持一个写操作;
- ArkTS侧支持的基本数据类型:number、string、二进制类型数据、boolean;
- 为保证插入并读取数据成功,一条数据不要超过2M;
4.2 接口说明
// 获得一个相关的RdbStore,操作关系型数据库,用户可以根据自己的需求配置RdbStore的参数,然后通过RdbStore调用相关接口可以执行相关的数据操作
getRdbStore(context: Context, config: StoreConfig, callback: AsyncCallback<RdbStore>): void
// 执行包含指定参数但不返回值的SQL语句
executeSql(sql: string, bindArgs: Array<ValueType>, callback: AsyncCallback<void>):void
// 向目标表中插入一行数据
insert(table: string, values: ValuesBucket, callback: AsyncCallback<number>):void
// 根据RdbPredicates的指定实例对象更新数据库中的数据
update(values: ValuesBucket, predicates: RdbPredicates, callback: AsyncCallback<number>):void |
// 根据RdbPredicates的指定实例对象从数据库中删除数据
delete(predicates: RdbPredicates, callback: AsyncCallback<number>):void
// 根据指定条件查询数据库中的数据
query(predicates: RdbPredicates, columns: Array<string>, callback: AsyncCallback<ResultSet>):void
// 删除数据库
deleteRdbStore(context: Context, name: string, callback: AsyncCallback<void>): void
4.3 举例说明
// 导入模块
import relationalStore from '@ohos.data.relationalStore';
// 获取context
let context = getContext(this);
export default class RDBStoreManager {
private rdbStore: relationalStore.RdbStore | null = null;
private tableName: string;
private sqlCreateTable: string;
private databaseName: string = '__Database.db';
constructor(tableName: string, sqlCreateTable: string) {
this.tableName = tableName;
this.sqlCreateTable = sqlCreateTable;
}
// 创建rdbStore
async getRdbStore() {
if (!this.rdbStore) {
console.log('RDBStoreManager', 'The rdbStore exists.');
return
}
try {
let rdb = await relationalStore.getRdbStore(context, {
name: this.databaseName, // 数据库文件名
securityLevel: relationalStore.SecurityLevel.S1, // 数据库安全级别
encrypt: false // 可选参数,是否加密,默认不加密
});
this.rdbStore = rdb;
this.rdbStore.executeSql(this.sqlCreateTable);
} catch (error) {
console.log('RDBStoreManager', `gerRdbStore() failed, err: ${error}`);
}
}
// 插入数据
async insertData(data: relationalStore.ValuesBucket): Promise<number | null> {
const valueBucket: relationalStore.ValuesBucket = data;
if (this.rdbStore) {
try {
let result = await this.rdbStore.insert(this.tableName, valueBucket);
return result;
} catch (error) {
console.log('RDBStoreManager', `insertData() failed, err: ${JSON.stringify(error)}`);
}
}
return null;
}
// 删除数据
async deleteData(predicates: relationalStore.RdbPredicates): Promise<number | null> {
if (this.rdbStore) {
try {
let result = await this.rdbStore.delete(predicates);
return result;
} catch (error) {
console.log('RDBStoreManager', `deleteData() failed, err: ${error}`);
}
}
return null;
}
// 更新数据
async updateData(predicates: relationalStore.RdbPredicates, data: relationalStore.ValuesBucket): Promise<number | null> {
if (this.rdbStore) {
try {
let result = await this.rdbStore.update(data, predicates);
return result;
} catch (error) {
console.log('RDBStoreManager', `updateData() failed, err: ${error}`);
}
}
return null;
}
// 查询数据
async query(predicates: relationalStore.RdbPredicates): Promise<relationalStore.ResultSet | null> {
if (this.rdbStore) {
try {
let result = await this.rdbStore.query(predicates)
return result;
} catch (error) {
console.log('RDBStoreManager', `query() failed, err: ${error}`);
}
}
return null;
}
}
在以上3个举例说明中,是自己根据业务需求封装的工具类,在实际使用过程中还需要进行一些更改,仅作参考。