ArkTS - 数据持久化

一、概述

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

持久(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 数据库来进行数据持久化处理的原因包括:

  1. 复杂数据结构: 如果你的应用需要存储的数据结构较为复杂,包含多个字段或需要进行多表关联,SQLite 提供了更灵活的表结构和查询语言,使你能够更好地组织和管理数据。

  2. 大量数据存储: 当应用需要存储大量结构化数据时,SQLite 可以更好地处理复杂的数据存储和检索需求。SharedPreferences适用于小型数据,但对于大量的、需要进行查询和排序的数据,SQLite 提供了更强大的功能。

  3. 查询和过滤: 如果你需要进行复杂的数据查询、排序、过滤操作,SQLite 提供了 SQL 查询语言,可以更方便地执行这些操作。

  4. 支持事务: 如果你的应用需要支持事务处理,SQLite 是一个更适合的选择。事务是一种保障数据库操作原子性、一致性、隔离性和持久性的机制。

  5. 多用户支持: 如果你的应用需要支持多用户场景,每个用户有自己的数据,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); // 创建数据表

      // 这里执行数据库的增、删、改、查等操作

    });
  }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/311815.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Unity编辑器扩展(外挂)

每日一句:未来的样子藏在现在的努力里 目录 什么是编译器开发 C#特性[System.Serializable] 特殊目录 命名空间 /*检视器属性控制*/ //添加变量悬浮提示文字 //给数值设定范围&#xff08;最小0&#xff0c;最大150&#xff09; //指定输入框&#xff0c;拥有5行 //默认…

机器学习激活函数

激活函数 激活函数是人工神经网络中的一个重要组成部分。它们用于向神经网络中添加非线性因素&#xff0c;使得网络能够解决复杂问题&#xff0c;如图像识别、语言处理等。激活函数的作用是决定一个神经元是否应该被激活&#xff0c;也就是说&#xff0c;它帮助决定神经元的输…

jupyter notebook 配置conda 虚拟环境python

conda创建python环境 conda create -n openvoice python3.9 激活环境 source activate openvoice 在虚拟环境中安装ipykernel pip install ipykernel 添加虚拟环境进到 jupyter notebook python -m ipykernel install --user --name openvoice --display-name openvoice …

计算机网络必考大题

TCP / IP 五层协议或OSI七层参考模型 CRC校验码&#xff08;也称为循环冗余码&#xff09; 1、根据生成多项式P(x)确定除数&#xff1b; 2、给生成多项式的P(x)的最高阶补0&#xff1b; 3、给信息位(补0后)与除数做异或运算&#xff0c;得到余数。 不相同为1 ^ 4、得到的余数补…

免费申请eu.org域名,开启个人网站之旅

介绍 eu.org的免费域名注册服务是由OpenTLD B.V.提供的。相比于其他免费域名注册服务&#xff0c;eu.org的域名后缀更加独特。同时&#xff0c;eu.org的域名注册也比较简单&#xff0c;只需要填写一些基本信息&#xff0c;就可以获得自己的免费域名。 注册账号 点击进入登…

如何在Github上快速下载代码

由于网络环境问题&#xff0c;有时候比较难从Github上下载代码&#xff0c;我归纳了以下三种从Github上下载代码的方法&#xff0c;如何选择使用&#xff0c;可根据你的实际情况&#xff1a; 目录 方法一&#xff1a;使用 “Download ZIP” 按钮 方法二&#xff1a;使用 Git…

代码随想录刷题笔记(DAY 10)

今日总结&#xff1a;快要期末考试了&#xff0c;现在在疯狂速成&#xff0c;今天稍微缓和了一点&#xff0c;应该能保证继续每天刷题&#xff0c;欠下的那些寒假补上。 Day 10 01. 用栈实现队列&#xff08;No. 232&#xff09; 题目链接 代码随想录题解 1.1 题目 请你仅…

【JAVA基础】JVM之类加载--双亲委派机制

目录 1. 类加载的过程描述&#xff1a;看图&#xff1a;解释&#xff1a; 2. 那么类加载器都有哪些呢3. 双亲委派机制3.1 双亲委派机制的过程3.2 图看委派过程3.3 为什么要设计双亲委派机制 4. 自定义类加载器4.1 如何定义自己的类加载器&#xff1f; 1. 类加载的过程 描述&am…

YOLOv8 + openVINO 多线程数据读写顺序处理

多线程数据读写顺序处理 一个典型的生产者-消费者模型&#xff0c;在这个模型中&#xff0c;多个工作线程并行处理从共享队列中获取的数据&#xff0c;并将处理结果以保持原始顺序的方式放入另一个队列。 多线程处理模型&#xff0c;具体细节如下&#xff1a; 1.数据:数据里必…

Java学习,一文掌握Java之SpringBoot框架学习文集(4)

&#x1f3c6;作者简介&#xff0c;普修罗双战士&#xff0c;一直追求不断学习和成长&#xff0c;在技术的道路上持续探索和实践。 &#x1f3c6;多年互联网行业从业经验&#xff0c;历任核心研发工程师&#xff0c;项目技术负责人。 &#x1f389;欢迎 &#x1f44d;点赞✍评论…

GBASE南大通用SQL API 中的 SQL

ESQL 产品为GBASE南大通用数据库 GBase 8s SQL API&#xff08;应用程序编程接口&#xff09;。 GBase 为 C 编程语言产生 SQL API。 下图展示 SQL API 产品如何工作。您编写您在其中将 SQL 语句处理作为可执行代码的源 程序。嵌入式 SQL 预处理器处理您的源程序&#xff0c;它…

【QML COOK】- 008-自定义属性

前面介绍了用C定义QML类型&#xff0c;通常在使用Qt Quick开发项目时&#xff0c;C定义后端数据类型&#xff0c;前端则完全使用QML实现。而QML类型或Qt Quick中的类型时不免需要为对象增加一些属性&#xff0c;本篇就来介绍如何自定义属性。 1. 创建项目&#xff0c;并编辑Ma…

Qt 6之六:Qt Designer介绍

Qt 6之六&#xff1a;Qt Designer介绍 Qt Designer是一个可视化的用户界面设计工具&#xff0c;用于创建Qt应用程序的用户界面&#xff0c;允许开发人员通过拖放和布局来设计和创建GUI界面。 Qt 6之一&#xff1a;简介、安装与简单使用 https://blog.csdn.net/cnds123/articl…

作用域与作用域链

作用域与作用域链 一、什么是作用域 作用域就是一个独立的代码区域&#xff0c;域内的变量不会暴露到外部&#xff0c;外部无法访问&#xff0c;也就是说具有隔离性。 function outFun() {var inVariable "内层变量2"; } outFun(); // inVariable 的作用域仅在函…

matlab绘图修改坐标轴数字字体大小及坐标轴自定义间隔设置

一、背景 在matlab使用plot函数绘图后&#xff0c;生成的图片坐标轴数字字体大小及间隔可能并不符合我们的要求&#xff0c;因此需要自定义修改&#xff0c;具体方法如下 二、修改坐标轴数字字体大小 只需添加以下命令即可&#xff1a; set(gca,FontName,Times New Roman,F…

echarts -- 柱状图之柱状条如何显示白色侧阴影且鼠标移入时高亮

有个图表是要求柱状条的右下侧显示一个白色的侧阴影&#xff0c;一直没找到合适的方法&#xff0c; 加border或者shadowColor都达不到需求的效果。 因为柱状图 中series里可以包含多组数据&#xff0c;有几组就代表一个系列中有几个数据。这就代表series里要写七组数据。 对于上…

你了解计算机网络的发展历史吗?

1.什么是计算机网络 计算机网络是指将一群具有独立功能的计算机通过通信设备以及传输媒体被互联起来的&#xff0c;在通信软件的支持下&#xff0c;实现计算机间资源共享、信息交换或协同工作的系统。计算机网络是计算机技术与通信技术紧密结合的产物&#xff0c;两者的迅速发展…

电阻如何读取阻值

前言&#xff1a;大家经常见到的贴片电阻上的丝印有纯数字、数字与R组合、数字与除R之外的字母组合的&#xff0c;但大家知不知道这样的标注与贴片电阻的i精度相关&#xff1f;同一个阻值因为精度不同&#xff0c;标注也会不同。例如封装为0805的贴片电阻&#xff0c;丝印473和…

STL-list的使用简介

目录 ​编辑 一、list的底层实现是带头双向循环链表 二、list的使用 1、4种构造函数&#xff08;与vector类似&#xff09;​编辑 2、迭代器iterator 3、容量&#xff08;capicity&#xff09;操作 4、element access 元素获取 5、增删查改 list modifiers 6、list的迭…

【计算机组成原理】-指令系统

&#x1f3b5;1.指令的发展 &#x1f308;1.1基础概念 计算机的程序是由一系列的机器指令组成的。指令就是要计算机执行某种操作的命令。从计算机的层次结构来说&#xff0c;有微指令&#xff0c;机器指令和宏指令之分。微程序中用到微指令&#xff0c;属于硬件&#xff0c;而…