精讲鸿蒙持久化存储(sqlite,preferences)实践
关系型数据库支持两种方式:恢复手动备份数据和恢复自动备份数据(仅系统应用可用),具体可见[关系型数据库数据恢复](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/data-backup-and-restore-V5#关系型数据库数据恢复)。await HistoryRecordModel.getInstance().
持久化存储(sqlite,preferences)实践
持久化存储-preferences
官方文档:
[https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/data-persistence-by-preferences-V5](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/data-persistence-by-preferences-V5)【新知识-用户首选项】- 学习流程
✔【用户首选项】- 数据持久化基本概念
**课程目标**- 认识首选项是什么?有什么作用?
- 认识首选项运行机制、限制约束
**什么是数据持久化?**将内存数据保存到磁盘或者数据库的过程,我们称为数据持久化
**持久化的作用:**可以保证数据永久留存,直到我们主动删除为止
用户首选项是什么?有什么用?
**用户首选项:**用户首选项支持持久化轻量级数据,并提供相关方法对其新增、修改、查询、删除
用户首选项运行机制:

限制约束:
- Key键为string类型,要求非空且长度不超过80个字节。
- 如果Value值为string类型,可以为空,不为空时长度不超过8192个字节,大约为8K大小
- 内存会随着存储数据量的增大而增大,所以存储的数据量应该是轻量级的,建议存储的数据不超过一万条,否则会在内存方面产生较大的开销。
✔【用户首选项】-新增/修改数据
课程目标
- 能利用getPreferencesSync + putSync + flush 向用户首选项文件store中新增/修改一条字符串数组数据:
["鸿蒙","HTML5"],key为keyword
新增/修改:如果首选项中没有数据则是【新增】、如果有数据则会被新值覆盖,则为【修改】
步骤:
- 在pages中创建一个pre_test.ets页面,在pre_test.ets页面上调用首选项api完成数据新增和修改需求
// 首选项api测试
import { preferences } from '@kit.ArkData'
@Entry
@Component
struct Pre_test {
build() {
Row() {
Column() {
// 1.0 利用首选项api对store这个文件做一个新增数据:["鸿蒙","HTML5"]
Button('新增/修改数据').onClick(() => {
// 1.0 获取store文件的操作对象
const pre = preferences.getPreferencesSync(getContext(),{name:'store'})
// 2.0 调用操作对象上的putSync方法完成数据的新增 (这是将数据保存到内存中)
pre.putSync('keyword',["鸿蒙Next","HTML5"])
pre.flush() //将内存数据写入到磁盘
AlertDialog.show({message:'首选项数据写入成功'})
})
}
.width('100%')
}
.height('100%')
}
}
- 修改EntryAbility.ets中的启动路径
import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { window } from '@kit.ArkUI';
export default class EntryAbility extends UIAbility {
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate');
}
onDestroy(): void {
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onDestroy');
}
onWindowStageCreate(windowStage: window.WindowStage): void {
// Main window is created, set main page for this ability
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate');
windowStage.loadContent('pages/pre_test', (err) => {
if (err.code) {
hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');
return;
}
hilog.info(0x0000, 'testTag', 'Succeeded in loading the content.');
});
}
onWindowStageDestroy(): void {
// Main window is destroyed, release UI related resources
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageDestroy');
}
onForeground(): void {
// Ability has brought to foreground
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onForeground');
}
onBackground(): void {
// Ability has back to background
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onBackground');
}
}
- 找到首选项磁盘目录查看数据

day6✔【用户首选项】-查询数据
课程目标
- 能利用getPreferencesSync + getSync查询指定key为keyword的数据
✔【用户首选项】-删除数据
课程目标
- 能利用getPreferencesSync + deleteSync + flush删除用户首选项文件中指定key为keyword的数据
【用户首选项】-综合案例
课程目标
- 使用用户首选项增,删,查 三套API实现如下需求:
- 在文本框输入关键字 回车后保存到用户首选项中
- 文本框回车保存的同时,查询用户首选项中的数据显示在页面列表中
- 点击 关键字后面的删除按钮,实现关键字的删除
- 点击全部删除按钮,实现删除所有关键字
提示:可以使用 Array数组形式来保存关键字
✔【综合案例】- 静态结构搭建
```arkts import { promptAction } from '@kit.ArkUI'@Entry
@Component
struct SearchPage {
@State keyword: string = ‘’
@StorageProp(‘topHeight’) topHeight: number = 0
@State isdel: boolean = false
aboutToAppear(): void {
}
@Builder
itemBuilder(text: string) {
Row({ space: 20 }) {
Text(text)
if (this.isdel) {
Text(‘x’)
.height(30)
.onClick(() => {
AlertDialog.show({ message: ‘删除’ + text })
})
}
}
.margin({ right: 10, top: 10 })
.padding({
left: 15,
right: 15,
top: 5,
bottom: 5
})
.backgroundColor(‘rgba(0,0,0,0.05)’)
.borderRadius(20)
}
build() {
Navigation() {
Column({ space: 15 }) {
// 1. 搜索关键字
TextInput({ placeholder: ‘输入回车保存数据’, text: $$this.keyword })
.onSubmit(() => {
AlertDialog.show({ message: this.keyword })
})
// 2. 关键字列表
Row() {
Text('搜索记录').fontSize(20).fontWeight(800)
Row() {
if (this.isdel) {
Text('全部删除')
.onClick(()=>{
AlertDialog.show({ message: '补上全部删除逻辑' })
})
Text(' | ')
Text('取消删除')
.onClick(()=>{
this.isdel = false
})
} else {
Image($r('app.media.ic_common_delete'))
.height(28)
.onClick(()=>{
this.isdel = true
})
}
}
}
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
// 3. 关键字列表
Flex({ wrap: FlexWrap.Wrap }) {
ForEach([1, 2, 3, 4], (item: number) => {
this.itemBuilder('文本' + item)
})
}
}
.padding(15)
}
.padding({ top: this.topHeight })
.titleMode(NavigationTitleMode.Mini)
.title('搜索页面')
}
}
<h3 id="BtdQj">✔【综合案例】-保存关键字数组</h3>
**课程目标**
+ 利用用户首选项getPreferencesSync+putSync+flush**追加保存**用户输入的关键字
+ 相同关键字不重复保存
注意:在调用putSync方法之前先调用getSync获取老数据数组,再使用push方法追加新数据后再保存回首选项
```arkts
import { promptAction } from '@kit.ArkUI'
import { preferences } from '@kit.ArkData'
@Entry
@Component
struct SerachPage {
@State keyword: string = ''
@StorageProp('topHeight') topHeight: number = 0
aboutToAppear(): void {
}
@Builder
itemBuilder(text: string) {
Row({ space: 20 }) {
Text(text)
Text('x')
.height(30)
.onClick(() => {
AlertDialog.show({ message: '删除' + text })
})
}
.margin({ right: 10, top: 10 })
.padding({ left: 15, right: 15, top: 5, bottom: 5 })
.backgroundColor('rgba(0,0,0,0.05)')
.borderRadius(20)
}
// 1.0 新增方法
async saveData(text: string) {
// 非空验证
if(!text){
return
}
// 1.0 获取首选项对象实例
const pre = preferences.getPreferencesSync(getContext(), { name: 'store1' })
// 2.0 调用putsync方法保存数组
// 2.0.1 先从首选项中获取老数据
let dataArr = pre.getSync('keyword', []) as string[]
// 判断如果首选项中已经有了该关键词,不再保存
if(dataArr.includes(text)){
// 该关键字已经存在了,不保存
return
}
dataArr.push(text)
// 2.0.2 存数据
pre.putSync('keyword', dataArr)
// 3.0 调用flush写入到磁盘
await pre.flush()
}
build() {
Column({ space: 15 }) {
// 1. 搜索关键字
TextInput({ placeholder: '输入回车保存数据', text: $$this.keyword })
.onSubmit(() => {
// AlertDialog.show({ message: this.keyword })
// 将关键词数据保存到首选项中
this.saveData(this.keyword)
})
// 2. 关键字列表
Row() {
Text('搜索记录').fontSize(20).fontWeight(800)
Row() {
Text('全部删除')
Text(' | ')
Text('取消删除')
Image($r('app.media.ic_common_delete'))
.height(28)
}
}
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
// 3. 关键字列表
Flex({ wrap: FlexWrap.Wrap }) {
ForEach([1, 2, 3, 4], (item: number) => {
this.itemBuilder('文本' + item)
})
}
}
.padding(15)
}
}
✔【综合案例】-查询保存的关键字数组
**课程目标**- 利用用户首选项getPreferencesSync+getSync从首选项中获取string[]类型的数据
**注意:**使用await调用,否则可能出现获取不到最新保存的关键字数据
import { promptAction } from '@kit.ArkUI'
import { preferences } from '@kit.ArkData'
@Entry
@Component
struct SearchPage {
@State keyword: string = ''
@StorageProp('topHeight') topHeight: number = 0
@State keywords: string[] = []
aboutToAppear() {
this.getData()
}
@Builder
itemBuilder(text: string) {
Row({ space: 20 }) {
Text(text)
Text('x')
.height(30)
.onClick(() => {
AlertDialog.show({ message: '删除' + text })
})
}
.margin({ right: 10, top: 10 })
.padding({ left: 15, right: 15, top: 5, bottom: 5 })
.backgroundColor('rgba(0,0,0,0.05)')
.borderRadius(20)
}
// 1.0 新增方法
async saveData(text: string) {
// 非空验证
if (!text) {
return
}
// 1.0 获取首选项对象实例
const pre = preferences.getPreferencesSync(getContext(), { name: 'store1' })
// 2.0 调用putsync方法保存数组
// 2.0.1 先从首选项中获取老数据
let dataArr = pre.getSync('keyword', []) as string[]
// 判断如果首选项中已经有了该关键词,不再保存
if (dataArr.includes(text)) {
// 该关键字已经存在了,不保存
return
}
dataArr.push(text)
// 2.0.2 存数据
pre.putSync('keyword', dataArr)
// 3.0 调用flush写入到磁盘
await pre.flush()
}
// 2.0 获取首选项数据
async getData() {
const pre = preferences.getPreferencesSync(getContext(), { name: 'store1' })
this.keywords = pre.getSync('keyword', []) as string[]
}
build() {
Column({ space: 15 }) {
// 1. 搜索关键字
TextInput({ placeholder: '输入回车保存数据', text: $$this.keyword })
.onSubmit(async () => {
// AlertDialog.show({ message: this.keyword })
// 将关键词数据保存到首选项中
// 注意使用await调用,否则可能出现获取不到最新保存的关键字数据
await this.saveData(this.keyword)
await this.getData()
})
// 2. 关键字列表
Row() {
Text('搜索记录').fontSize(20).fontWeight(800)
Row() {
Text('全部删除')
Text(' | ')
Text('取消删除')
Image($r('app.media.ic_common_delete'))
.height(28)
}
}
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
// 3. 关键字列表
Flex({ wrap: FlexWrap.Wrap }) {
ForEach(this.keywords, (item:string) => {
this.itemBuilder(item)
})
}
}
.padding(15)
}
}
✔【综合案例】-删除指定关键字
**课程目标**- 利用用户首选项getPreferencesSync+putSync+flush删除指定关键字
- 利用数组方法
findIndex查找关键字在数组中的索引和使用splice根据索引从数组中删除一个数据
import { promptAction } from '@kit.ArkUI'
import { preferences } from '@kit.ArkData'
@Entry
@Component
struct SearchPage {
@State keyword: string = ''
@StorageProp('topHeight') topHeight: number = 0
@State keywords: string[] = []
aboutToAppear() {
this.getData()
}
@Builder
itemBuilder(text: string) {
Row({ space: 20 }) {
Text(text)
Text('x')
.height(30)
.onClick(async () => {
// 删除指定关键字
await this.delData(text)
await this.getData()
})
}
.margin({ right: 10, top: 10 })
.padding({ left: 15, right: 15, top: 5, bottom: 5 })
.backgroundColor('rgba(0,0,0,0.05)')
.borderRadius(20)
}
// 1.0 新增方法
async saveData(text: string) {
// 非空验证
if (!text) {
return
}
// 1.0 获取首选项对象实例
const pre = preferences.getPreferencesSync(getContext(), { name: 'store1' })
// 2.0 调用putsync方法保存数组
// 2.0.1 先从首选项中获取老数据
let dataArr = pre.getSync('keyword', []) as string[]
// 判断如果首选项中已经有了该关键词,不再保存
if (dataArr.includes(text)) {
// 该关键字已经存在了,不保存
return
}
dataArr.push(text)
// 2.0.2 存数据
pre.putSync('keyword', dataArr)
// 3.0 调用flush写入到磁盘
await pre.flush()
}
// 2.0 获取首选项数据
async getData() {
const pre = preferences.getPreferencesSync(getContext(), { name: 'store1' })
this.keywords = pre.getSync('keyword', []) as string[]
}
// 3.0 删除指定关键字和全部关键字
async delData(text?: string) {
const pre = preferences.getPreferencesSync(getContext(), { name: 'store1' })
// 1.0 删除全部关键
if (!text) {
pre.deleteSync('keyword')
} else {
// 2.0 删除指定关键字 获取 -> 删除内存数组的关键字 -> 写回
let datas = pre.getSync('keyword', []) as string[]
let cindex = datas.findIndex(item => item === text)
// 当关键字索引为-1的时候,表示没有找到
if (cindex < 0) {
return
}
// 如果有删除
datas.splice(cindex, 1) // 返回值表示删除的元素
// 保存回去
pre.putSync('keyword', datas)
}
// 写回磁盘
pre.flush()
}
build() {
Column({ space: 15 }) {
// 1. 搜索关键字
TextInput({ placeholder: '输入回车保存数据', text: $$this.keyword })
.onSubmit(async () => {
// AlertDialog.show({ message: this.keyword })
// 将关键词数据保存到首选项中
await this.saveData(this.keyword)
await this.getData()
})
// 2. 关键字列表
Row() {
Text('搜索记录').fontSize(20).fontWeight(800)
Row() {
Text('全部删除')
.onClick(async () => {
// 删除全局数据
await this.delData()
await this.getData()
})
Text(' | ')
Text('取消删除')
Image($r('app.media.ic_common_delete'))
.height(28)
}
}
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
// 3. 关键字列表
Flex({ wrap: FlexWrap.Wrap }) {
ForEach(this.keywords, (item: string) => {
this.itemBuilder(item)
})
}
}
.padding(15)
}
}
✔【综合案例】-删除全部关键字
**课程目标**- 利用用户首选项getPreferencesSync+deleteSync+flush从删除首选项中全部数据
import { promptAction } from '@kit.ArkUI'
import { preferences } from '@kit.ArkData'
@Entry
@Component
struct SearchPage {
@State keyword: string = ''
@StorageProp('topHeight') topHeight: number = 0
@State keywords: string[] = []
aboutToAppear() {
this.getData()
}
@Builder
itemBuilder(text: string) {
Row({ space: 20 }) {
Text(text)
Text('x')
.height(30)
.onClick(async () => {
// 删除指定关键字
await this.delData(text)
await this.getData()
})
}
.margin({ right: 10, top: 10 })
.padding({ left: 15, right: 15, top: 5, bottom: 5 })
.backgroundColor('rgba(0,0,0,0.05)')
.borderRadius(20)
}
// 1.0 新增方法
async saveData(text: string) {
// 非空验证
if (!text) {
return
}
// 1.0 获取首选项对象实例
const pre = preferences.getPreferencesSync(getContext(), { name: 'store1' })
// 2.0 调用putsync方法保存数组
// 2.0.1 先从首选项中获取老数据
let dataArr = pre.getSync('keyword', []) as string[]
// 判断如果首选项中已经有了该关键词,不再保存
if (dataArr.includes(text)) {
// 该关键字已经存在了,不保存
return
}
dataArr.push(text)
// 2.0.2 存数据
pre.putSync('keyword', dataArr)
// 3.0 调用flush写入到磁盘
await pre.flush()
}
// 2.0 获取首选项数据
async getData() {
const pre = preferences.getPreferencesSync(getContext(), { name: 'store1' })
this.keywords = pre.getSync('keyword', []) as string[]
}
// 3.0 删除指定关键字和全部关键字
async delData(text?: string) {
const pre = preferences.getPreferencesSync(getContext(), { name: 'store1' })
// 1.0 删除全部关键
if (!text) {
pre.deleteSync('keyword')
} else {
// 2.0 删除指定关键字 获取 -> 删除内存数组的关键字 -> 写回
let datas = pre.getSync('keyword', []) as string[]
let cindex = datas.findIndex(item => item === text)
// 当关键字索引为-1的时候,表示没有找到
if (cindex < 0) {
return
}
// 如果有删除
datas.splice(cindex, 1) // 返回值表示删除的元素
// 保存回去
pre.putSync('keyword', datas)
}
// 写回磁盘
pre.flush()
}
build() {
Column({ space: 15 }) {
// 1. 搜索关键字
TextInput({ placeholder: '输入回车保存数据', text: $$this.keyword })
.onSubmit(async () => {
// AlertDialog.show({ message: this.keyword })
// 将关键词数据保存到首选项中
await this.saveData(this.keyword)
await this.getData()
})
// 2. 关键字列表
Row() {
Text('搜索记录').fontSize(20).fontWeight(800)
Row() {
Text('全部删除')
.onClick(async () => {
// 删除全局数据
await this.delData()
await this.getData()
})
Text(' | ')
Text('取消删除')
Image($r('app.media.ic_common_delete'))
.height(28)
}
}
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
// 3. 关键字列表
Flex({ wrap: FlexWrap.Wrap }) {
ForEach(this.keywords, (item: string) => {
this.itemBuilder(item)
})
}
}
.padding(15)
}
}
其他:
示例代码:
[https://gitee.com/harmonyos_samples/preferences.git](https://gitee.com/harmonyos_samples/preferences.git)
1.点击顶部titleBar的右侧切换按钮,弹出主题菜单,选择任意主题则切换相应的主题界面;
2.退出应用再重新进入,显示上一次退出前的主题界面。
封装的单例:
```typescript import { preferences } from "@kit.ArkData"export default class PreferencesDemo {
private static Instance: PreferencesDemo | null = null
pref: preferences.Preferences | null = null
private constructor() {
}
static getInstance() {
if (!PreferencesDemo.Instance) {
PreferencesDemo.Instance = new PreferencesDemo()
}
return PreferencesDemo.Instance
}
async loadPreferences(context: Context, name: string) {
let pre = await preferences.getPreferences(context, name)
// todo 获取用户首选项对象
this.pref = pre
}
// todo 获取键值对
async getPreferences(key: string, value: preferences.ValueType): Promise<preferences.ValueType> {
return new Promise<preferences.ValueType>(async (resolve, reject) => {
if (!this.pref) {
console.log(‘当前不存在用户首选项’)
reject()
} else {
let ret = await this.pref.get(key, value)
resolve(ret)
}
})
}
// todo 添加键值对
async pushPreferences(key: string, value: preferences.ValueType) {
if (!this.pref) {
console.log(‘当前不存在用户首选项’)
} else {
await this.pref.put(key, value)
await this.pref.flush()
// 立即刷新写入文件中
}
}
// todo 删除键值对
async deletePreferences(key: string) {
if (!this.pref) {
console.log(‘当前不存在用户首选项’)
} else {
await this.pref.delete(key)
}
}
// todo 查询所有键值对
getAllPreferences(): Promise {
return new Promise(async (resolve, reject) => {
if (!this.pref) {
console.log(‘当前不存在用户首选项’)
reject()
} else {
let ret = await this.pref.getAll()
resolve(ret)
}
})
}
// todo 清空数据
async clear() {
try {
let value = await this.pref?.clear()
console.log(‘清空Preferences成功’)
return value
} catch (e) {
console.log(‘清空Preferences失败’, JSON.stringify(e))
}
}
}
<h4 id="FMYsn">封装单例使用:</h4>
```typescript
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) 中添加代码:
PreferencesDemo.getInstance().loadPreferences(this.context, 'xy')
import { promptAction, router } from '@kit.ArkUI';
import PreferencesDemo from '../day09/PreferencesDemo';
import { preferences } from '@kit.ArkData';
import { JSON } from '@kit.ArkTS';
@Entry
@Component
struct Day03_demo4Page {
@State message: string = 'Hello World';
@State appName: string = '美团';
@State ft_color: Color = Color.Gray
@State ifAgree: boolean = false
@State phone: string = ''
async aboutToAppear() {
// 1.0 获取首选项对象实例
const pre = await preferences.getPreferencesSync(getContext(), { name: 'xy' })
// 2.0 调用putsync方法保存数组
// 2.0.1 先从首选项中获取老数据
let dataArr = await pre.getSync('phone', '') as string
// promptAction.showToast({ message: dataArr })
// const pre2 = await preferences.getPreferencesSync(getContext(), { name: 'xy' })
// // 2.0 调用putsync方法保存数组
// // 2.0.1 先从首选项中获取老数据
// let dataArr2 = await pre2.getSync('keyword', []) as string[]
// promptAction.showToast({ message: 'arr2' + JSON.stringify(dataArr2) })
let phone2 = (await PreferencesDemo.getInstance().getPreferences('phone', '')).toString()
promptAction.showToast({ message: phone2 })
if (phone2.length) {
this.phone = phone2
}
}
build() {
Column() {
Stack({ alignContent: Alignment.BottomEnd }) {
Column() {
Text(`欢迎登陆${this.appName}`).fontSize(30).width('100%')
Row() {
Text('+86')
Image($r('app.media.arrow_down')).width(20).height(20)
TextInput({ placeholder: '请输入手机号', text: $$this.phone })
.type(InputType.Number)
.backgroundColor(Color.Transparent)
.fontColor(Color.Black)
.width('80%')
}.width('100%').margin({ top: 40 })
Row().width('100%').height(1).backgroundColor(Color.Black)
TextInput({ placeholder: '请输入密码' })
.type(InputType.Password)
.width('100%')
.backgroundColor(Color.Transparent)
.margin({ top: 20 })
Row().width('100%').height(1).backgroundColor(Color.Black)
Flex({
direction: FlexDirection.Row,
wrap: FlexWrap.Wrap,
alignItems: ItemAlign.Center
}) {
Toggle({ type: ToggleType.Checkbox, isOn: false })
.onChange(() => {
this.ifAgree = !this.ifAgree
})
Text('我已阅读并同意').fontColor(this.ft_color)
Text(`《${this.appName}用户协议》`).fontColor(Color.Blue)
Text('和').fontColor(this.ft_color)
Text('《隐私政策》').fontColor(Color.Blue)
}.width('100%').margin({ top: 20 })
Button('登陆')
.width('100%')
.backgroundColor('#fff6b9')
.fontColor(Color.Black)
.margin({ top: 20 })
.onClick(async () => {
promptAction.showToast({ message: this.phone + '保存数据到本地' })
await PreferencesDemo.getInstance().pushPreferences('phone', this.phone)
})
Button('新增/修改数据')
.width('100%')
.backgroundColor('#fff6b9')
.fontColor(Color.Black)
.margin({ top: 20 })
.onClick((event: ClickEvent) => {
// 1.0 获取store文件的操作对象
const pre = preferences.getPreferencesSync(getContext(), { name: 'store' })
// 2.0 调用操作对象上的putSync方法完成数据的新增 (这是将数据保存到内存中)
pre.putSync('keyword', ["鸿蒙Next", "HTML5"])
pre.flush() //将内存数据写入到磁盘
AlertDialog.show({ message: '首选项数据写入成功' })
})
Row() {
Text('短信验证码登陆').fontColor('#3A8BFF').onClick(() => {
router.pushUrl({ url: 'pages/day03/day03_demo5Page' })
})
Text('遇到问题').fontColor('#3A8BFF')
}.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
.margin({ top: 20 })
}.height('100%')
.width('100%')
Row() {
Image($r("app.media.dianxin")).width(40).height(40)
Image($r("app.media.wechat")).width(40).height(40)
Image($r("app.media.QQ")).width(40).height(40)
Image($r("app.media.apple")).width(40).height(40)
}.justifyContent(FlexAlign.SpaceBetween)
.width('100%')
.padding(20)
}
}.padding({ left: 30, right: 30 })
.height('100%')
.width('100%')
}
}
**再put后沙盒才会有数据**
sqlite数据库
官方文档:
[https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/data-persistence-by-rdb-store-V5](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/data-persistence-by-rdb-store-V5)开发步骤:
1.使用关系型数据库实现数据持久化,需要获取一个RdbStore,
其中包括建库、建表、升降级等操作。示例代码如下所示:import { relationalStore } from '@kit.ArkData'; // 导入模块
import { UIAbility } from '@kit.AbilityKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { window } from '@kit.ArkUI';
// 此处示例在Ability中实现,使用者也可以在其他合理场景中使用
class EntryAbility extends UIAbility {
onWindowStageCreate(windowStage: window.WindowStage) {
const STORE_CONFIG :relationalStore.StoreConfig= {
name: 'RdbTest.db', // 数据库文件名
securityLevel: relationalStore.SecurityLevel.S3, // 数据库安全级别
encrypt: false, // 可选参数,指定数据库是否加密,默认不加密
customDir: 'customDir/subCustomDir', // 可选参数,数据库自定义路径。数据库将在如下的目录结构中被创建:context.databaseDir + '/rdb/' + customDir,其中context.databaseDir是应用沙箱对应的路径,'/rdb/'表示创建的是关系型数据库,customDir表示自定义的路径。当此参数不填时,默认在本应用沙箱目录下创建RdbStore实例。
isReadOnly: false // 可选参数,指定数据库是否以只读方式打开。该参数默认为false,表示数据库可读可写。该参数为true时,只允许从数据库读取数据,不允许对数据库进行写操作,否则会返回错误码801。
};
// 判断数据库版本,如果不匹配则需进行升降级操作
// 假设当前数据库版本为3,表结构:EMPLOYEE (NAME, AGE, SALARY, CODES, IDENTITY)
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, IDENTITY UNLIMITED INT)'; // 建表Sql语句, IDENTITY为bigint类型,sql中指定类型为UNLIMITED INT
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.');
// 当数据库创建时,数据库默认版本为0
if (store.version === 0) {
store.executeSql(SQL_CREATE_TABLE); // 创建数据表
// 设置数据库的版本,入参为大于0的整数
store.version = 3;
}
// 如果数据库版本不为0且和当前数据库版本不匹配,需要进行升降级操作
// 当数据库存在并假定版本为1时,例应用从某一版本升级到当前版本,数据库需要从1版本升级到2版本
if (store.version === 1) {
// version = 1:表结构:EMPLOYEE (NAME, SALARY, CODES, ADDRESS) => version = 2:表结构:EMPLOYEE (NAME, AGE, SALARY, CODES, ADDRESS)
(store as relationalStore.RdbStore).executeSql('ALTER TABLE EMPLOYEE ADD COLUMN AGE INTEGER');
store.version = 2;
}
// 当数据库存在并假定版本为2时,例应用从某一版本升级到当前版本,数据库需要从2版本升级到3版本
if (store.version === 2) {
// version = 2:表结构:EMPLOYEE (NAME, AGE, SALARY, CODES, ADDRESS) => version = 3:表结构:EMPLOYEE (NAME, AGE, SALARY, CODES)
(store as relationalStore.RdbStore).executeSql('ALTER TABLE EMPLOYEE DROP COLUMN ADDRESS TEXT');
store.version = 3;
}
});
// 请确保获取到RdbStore实例后,再进行数据库的增、删、改、查等操作
}
}
说明
2.获取到RdbStore后,调用insert()接口插入数据。
示例代码如下所示:let store: relationalStore.RdbStore | undefined = undefined;
let value1 = 'Lisa';
let value2 = 18;
let value3 = 100.5;
let value4 = new Uint8Array([1, 2, 3, 4, 5]);
let value5 = BigInt('15822401018187971961171');
// 以下三种方式可用
const valueBucket1: relationalStore.ValuesBucket = {
'NAME': value1,
'AGE': value2,
'SALARY': value3,
'CODES': value4,
'IDENTITY': value5,
};
const valueBucket2: relationalStore.ValuesBucket = {
NAME: value1,
AGE: value2,
SALARY: value3,
CODES: value4,
IDENTITY: value5,
};
const valueBucket3: relationalStore.ValuesBucket = {
"NAME": value1,
"AGE": value2,
"SALARY": value3,
"CODES": value4,
"IDENTITY": value5,
};
if (store !== undefined) {
(store as relationalStore.RdbStore).insert('EMPLOYEE', valueBucket1, (err: BusinessError, rowId: number) => {
if (err) {
console.error(`Failed to insert data. Code:${err.code}, message:${err.message}`);
return;
}
console.info(`Succeeded in inserting data. rowId:${rowId}`);
})
}
说明
关系型数据库没有显式的flush操作实现持久化,数据插入即保存在持久化文件。
3.根据谓词指定的实例对象,对数据进行修改或删除。
调用update()方法修改数据,调用delete()方法删除数据。示例代码如下所示:let value6 = 'Rose';
let value7 = 22;
let value8 = 200.5;
let value9 = new Uint8Array([1, 2, 3, 4, 5]);
let value10 = BigInt('15822401018187971967863');
// 以下三种方式可用
const valueBucket4: relationalStore.ValuesBucket = {
'NAME': value6,
'AGE': value7,
'SALARY': value8,
'CODES': value9,
'IDENTITY': value10,
};
const valueBucket5: relationalStore.ValuesBucket = {
NAME: value6,
AGE: value7,
SALARY: value8,
CODES: value9,
IDENTITY: value10,
};
const valueBucket6: relationalStore.ValuesBucket = {
"NAME": value6,
"AGE": value7,
"SALARY": value8,
"CODES": value9,
"IDENTITY": value10,
};
// 修改数据
let predicates1 = new relationalStore.RdbPredicates('EMPLOYEE'); // 创建表'EMPLOYEE'的predicates
predicates1.equalTo('NAME', 'Lisa'); // 匹配表'EMPLOYEE'中'NAME'为'Lisa'的字段
if (store !== undefined) {
(store as relationalStore.RdbStore).update(valueBucket4, predicates1, (err: BusinessError, rows: number) => {
if (err) {
console.error(`Failed to update data. Code:${err.code}, message:${err.message}`);
return;
}
console.info(`Succeeded in updating data. row count: ${rows}`);
})
}
// 删除数据
predicates1 = new relationalStore.RdbPredicates('EMPLOYEE');
predicates1.equalTo('NAME', 'Lisa');
if (store !== undefined) {
(store as relationalStore.RdbStore).delete(predicates1, (err: BusinessError, rows: number) => {
if (err) {
console.error(`Failed to delete data. Code:${err.code}, message:${err.message}`);
return;
}
console.info(`Delete rows: ${rows}`);
})
}
4.根据谓词指定的查询条件查找数据。
调用query()方法查找数据,返回一个ResultSet结果集。示例代码如下所示:let predicates2 = new relationalStore.RdbPredicates('EMPLOYEE');
predicates2.equalTo('NAME', 'Rose');
if (store !== undefined) {
(store as relationalStore.RdbStore).query(predicates2, ['ID', 'NAME', 'AGE', 'SALARY', 'IDENTITY'], (err: BusinessError, resultSet) => {
if (err) {
console.error(`Failed to query data. Code:${err.code}, message:${err.message}`);
return;
}
console.info(`ResultSet column names: ${resultSet.columnNames}, column count: ${resultSet.columnCount}`);
// resultSet是一个数据集合的游标,默认指向第-1个记录,有效的数据从0开始。
while (resultSet.goToNextRow()) {
const id = resultSet.getLong(resultSet.getColumnIndex('ID'));
const name = resultSet.getString(resultSet.getColumnIndex('NAME'));
const age = resultSet.getLong(resultSet.getColumnIndex('AGE'));
const salary = resultSet.getDouble(resultSet.getColumnIndex('SALARY'));
const identity = resultSet.getValue(resultSet.getColumnIndex('IDENTITY'));
console.info(`id=${id}, name=${name}, age=${age}, salary=${salary}, identity=${identity}`);
}
// 释放数据集的内存
resultSet.close();
})
}
说明
当应用完成查询数据操作,不再使用结果集(ResultSet)时,请及时调用close方法关闭结果集,释放系统为其分配的内存。
5.在同路径下备份数据库。
关系型数据库支持两种手动备份和自动备份(仅系统应用可用)两种方式,具体可见[关系型数据库备份](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/data-backup-and-restore-V5#关系型数据库备份)。此处以手动备份为例:
if (store !== undefined) {
// "Backup.db"为备份数据库文件名,默认在RdbStore同路径下备份。也可指定路径:customDir + "backup.db"
(store as relationalStore.RdbStore).backup("Backup.db", (err: BusinessError) => {
if (err) {
console.error(`Failed to backup RdbStore. Code:${err.code}, message:${err.message}`);
return;
}
console.info(`Succeeded in backing up RdbStore.`);
})
}
6.从备份数据库中恢复数据。
关系型数据库支持两种方式:恢复手动备份数据和恢复自动备份数据(仅系统应用可用),具体可见[关系型数据库数据恢复](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/data-backup-and-restore-V5#关系型数据库数据恢复)。此处以调用restore接口恢复手动备份数据为例:
if (store !== undefined) {
(store as relationalStore.RdbStore).restore("Backup.db", (err: BusinessError) => {
if (err) {
console.error(`Failed to restore RdbStore. Code:${err.code}, message:${err.message}`);
return;
}
console.info(`Succeeded in restoring RdbStore.`);
})
}
7.删除数据库。
调用deleteRdbStore()方法,删除数据库及数据库相关文件。示例代码如下:relationalStore.deleteRdbStore(this.context, 'RdbTest.db', (err: BusinessError) => {
if (err) {
console.error(`Failed to delete RdbStore. Code:${err.code}, message:${err.message}`);
return;
}
console.info('Succeeded in deleting RdbStore.');
});
具体案例:

export default class EntryAbility extends UIAbility {
async onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): Promise<void> {
//this.context.getApplicationContext().setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_NOT_SET);
//hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate');
//PreferencesDemo.getInstance().loadPreferences(this.context, 'xy')
await DbUtilsDemo.initDB(this.context)
// await dbUtil.createTable(`create table IF NOT EXISTS happy(name varchar(16),pd INTEGER,id INTEGER PRIMARY KEY AUTOINCREMENT)`)
await DbUtilsDemo.createTable(`CREATE TABLE IF NOT EXISTS water(
nickname VARCHAR(45) NULL,
avatar VARCHAR(200) NULL,
cover VARCHAR(200) NULL,
likedCount VARCHAR(15) NULL,
displayTitle VARCHAR(45) NULL,
id INTEGER PRIMARY KEY AUTOINCREMENT)`)
}
WaterFlowItem.ets
```typescript export interface WaterFlowItem { nickName: string, avatar: string, cover: string, likedCount: string, displayTitle: string } ```History.ets
```typescript export interface History { name: string pd: string } ```DbUtilsDemo.ets
```typescript import { relationalStore } from '@kit.ArkData' import { common } from '@kit.AbilityKit' import { JSON } from '@kit.ArkTS' import { History } from '../common/History'const config: relationalStore.StoreConfig = {
name: ‘xy’, //数据库名称
securityLevel: relationalStore.SecurityLevel.S1 //数据库安全等级
}
class DbUtilsDemo {
rdbStore: relationalStore.RdbStore | null = null
// 创建sqlite对象
// userId用于决定不同用户使用不同的数据库
initDB(context: common.UIAbilityContext | common.Context, userId?: string): Promise {
return new Promise((resolve, reject) => {
// 获取数据库对象
relationalStore.getRdbStore(context, config)
.then(rdbStore => {
this.rdbStore = rdbStore
// 赋值给数据库对象
console.log(‘rdbStore 初始化完成!’, JSON.stringify(rdbStore))
resolve()
})
.catch((reason: Error) => {
console.log(‘rdbStore 初始化异常’, JSON.stringify(reason))
reject(reason)
})
})
}
createTable(createSQL: string): Promise {
return new Promise((resolve, reject) => {
this.rdbStore?.executeSql(createSQL)
.then(() => {
console.log(‘创建表成功’, createSQL)
resolve()
})
.catch((err: Error) => {
console.log(‘创建表失败,’ + err.message, JSON.stringify(err))
reject(err)
})
})
}
}
let dbUtil: DbUtilsDemo = new DbUtilsDemo();
export default dbUtil as DbUtilsDemo
<h4 id="nXJxc">HistoryRecordModel.ets</h4>
```typescript
// import { History } from "./History"
import { relationalStore, ValuesBucket } from "@kit.ArkData"
import DbUtilsDemo from "../utils/DbUtilsDemo"
import dbUtil from "../utils/DbUtilsDemo"
import { Type } from "@kit.ArkUI"
import { WaterFlowItem } from '../pages/day08/das'
type History = WaterFlowItem
export class HistoryRecordModel {
private static Instance: HistoryRecordModel | null = null
TABNAME: string = 'water'
static getInstance() {
if (!HistoryRecordModel.Instance) {
HistoryRecordModel.Instance = new HistoryRecordModel()
}
return HistoryRecordModel.Instance
}
// todo 应为insert操作 需要修改ValuesBucket类型
valueBucket(value: History): ValuesBucket {
let obj: ValuesBucket = {}
if (!value) {
return {}
} else {
obj.nickName = value.nickName
obj.avatar = value.avatar
obj.cover = value.cover
obj.likedCount = value.likedCount
obj.displayTitle = value.displayTitle
return obj
}
}
async insert(record: History): Promise<number> {
return new Promise((resolve, reject) => {
// 获取数据库对象进行判断
if (DbUtilsDemo.rdbStore) {
let valueRecord: ValuesBucket = this.valueBucket(record)
console.log("listItem的值", JSON.stringify(valueRecord))
DbUtilsDemo.rdbStore?.insert(this.TABNAME, valueRecord, (err, rowId: number) => {
if (err) {
console.error(`sql失败插入:${err.message}, message:${err.name}`);
reject(err)
// reject(err)
} else {
console.info(`sql成功插入:${rowId}`);
resolve(rowId)
}
})
}
})
}
// todo 删除操作
deleteById(name: string): Promise<number> {
return new Promise((resolve, reject) => {
if (DbUtilsDemo.rdbStore) {
let predicates = new relationalStore.RdbPredicates(this.TABNAME)
// 选择表操作
predicates.equalTo('name', name)
// todo 进行等值比较
DbUtilsDemo.rdbStore?.delete(predicates, (err, rows: number) => {
if (err) {
console.log(`sql删除失败.Code:${err.code},message:${err.message}`)
reject(err)
} else {
console.log(`sql删除成功:${rows}`)
resolve(rows)
}
})
}
})
}
//
// /**
// * 查询函数
// * 参数不填则全局查询默认100条,按照更新时间降序
// *
// *
// * date为日期 格式 '2024-03-12 15:30:12'
// *
// *
// *
// *
// *
// * @param limit
// * @returns
// *
// * */
query(limit = 100): Promise<History[]> {
let historys: Array<History> = []
return new Promise((resolve, reject) => {
// predicates.orderByDesc("UPDATE_TIME")
if (DbUtilsDemo.rdbStore) {
let predicates = new relationalStore.RdbPredicates(this.TABNAME);
predicates.limitAs(limit)
// 单次查询最多100条数据
// predicates.orderByDesc("update_time")
// 表示以那个属性进行倒序
predicates.distinct()
// 表示去重
DbUtilsDemo.rdbStore.query(predicates, (err, resultSet) => {
if (err) {
console.error(`Failed to query data. Code:${err.code}, message:${err.message}`);
reject(err)
} else {
console.info(`ResultSet column names: ${resultSet.columnNames}, column count: ${resultSet.columnCount}`);
// resultSet是一个数据集合的游标,默认指向第-1个记录,有效的数据从0开始。
while (resultSet.goToNextRow()) {
const id = resultSet.getLong(resultSet.getColumnIndex('id'));
const nickName = resultSet.getString(resultSet.getColumnIndex('nickname'));
const avatar = resultSet.getString(resultSet.getColumnIndex('avatar'));
const cover = resultSet.getString(resultSet.getColumnIndex('cover'));
const likedCount = resultSet.getString(resultSet.getColumnIndex('likedCount'));
const displayTitle = resultSet.getString(resultSet.getColumnIndex('displayTitle'));
historys.push({
nickName: nickName,
avatar: avatar,
cover: cover,
likedCount: likedCount,
displayTitle: displayTitle
})
}
// 释放数据集的内存
console.log("history记录信息", JSON.stringify(historys))
resultSet.close();
resolve(historys)
}
})
}
// resolve(historys)
})
}
// /**
// * record参数类型为
// * 根据id来更新数据
// * @param record
// * @returns
// **/
update(record: History, name: string): Promise<number> {
return new Promise((resolve, reject) => {
if (DbUtilsDemo.rdbStore) {
let valueRecord = this.valueBucket(record)
let predicates = new relationalStore.RdbPredicates(this.TABNAME); // 创建表的predicates
predicates.equalTo('name', name); // 匹配表'name'的字段
DbUtilsDemo.rdbStore?.update(valueRecord, predicates, (err, rows: number) => {
if (err) {
console.error(`更新失败 data. Code:${err.code}, message:${err.message}`);
reject(err)
} else {
console.info(`更新成功 in updating data. row count: ${rows}`);
resolve(rows)
}
})
}
})
}
}
SQLPage.ets
```typescript import { History } from '../../common/History'; import { HistoryRecordModel } from '../../common/HistoryRecordModel'; import { it } from '../day05/item_cmp'; import { WaterFlowItem } from '../day08/das';@Entry
@Component
struct SQLPage {
@State message: string = ‘Hello World’;
@State historys: Array = []
aboutToAppear(): void {
}
build() {
Column() {
Text()
Button(‘添加sqlite数据’)
.onClick(async () => {
await HistoryRecordModel.getInstance().insert({ name: ‘小黑子’, pd: ‘123456’ })
})
Button(‘删除sqlite数据’)
.onClick(async () => {
await HistoryRecordModel.getInstance().deleteById(‘小黑子’)
})
Button(‘查询sqlite数据’)
.onClick(async (event: ClickEvent) => {
let items = await HistoryRecordModel.getInstance().query(10)
items.forEach((value: WaterFlowItem) => {
this.historys.push({ name: value.nickName, pd: value.cover })
})
// this.historys = await HistoryRecordModel.getInstance().query(10)
})
ForEach(this.historys, (item: History) => {
Text(`id:${item.name} pd: ${item.pd}`)
})
}
.height('100%')
.width('100%')
}
}

更多推荐



所有评论(0)