当前位置: 首页 > news >正文

HarmonyOS开发(九):数据管理

1、概述

1.1、功能简介

数据管理为开发者提供数据存储、数据管理能力。

它分为两个部分:

  • 数据存储:提供通用数据持久化能力,根据数据特点,分为用户首选项、键值型数据库和关系型数据库。
  • 数据管理:提供高效的数据管理能力,包括权限管理、数据备分恢复、数据共享等

注:应用创建的数据库,都保存到应用少盒,当应用卸载时,数据库也会自动删除。

1.2、运作机制

数据管理模块包括用户首选项、键值型数据管理、关系型数据管理、分布式数据对象和跨应用数据管理。

Interface接口层提供标准JS API接口

Frameworks&System service层负责实现部件数据存储功能

另外还有一些SQLite和其它子系统的依赖

  • 用户首选项:Preferences, 提供轻量级配置数据持久化能力,支持订阅数据变化的通知能力。不支持分布式同步,常常用来保存应用配置信息、用户偏好设置等
  • 键值型数据管理:KV-Store,提供了键值型数据库的读写、加密、手动备份能力。暂不支持分布式功能。
  • 关系型数据管理:RelationalStore,提供了关系型数据库的增删改查、加密、手动备份能力。暂不支持分布式功能。
  • 分布式数据对象:DataObject,独立提供地象型结构数据的分布式能力,暂不支持分布式功能。
  • 跨应用数据管理:DataShare,提供了向其他应用共享以及管理其数据的方法。仅系统应用可用。

2、应用数据持久化概述

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

HarmonyOS标准系统支持典型的存储数据形态有:用户首选项、键值型数据库、关系型数据库

3、用户首选项实现数据持久化

用户首选项为应用提供key-value键值型的数据处理能力,支持应用持久化轻量级数据,并对其进行修改和查询。

Preferences不适合存放过多数据,适用的场景一般为应用保存用户个性化的配置。

3.1、运作机制

用户程序通过JS接口调用用户首选项读写对应的数据文件。开发者可以把用户首选项持久化文件的内容加载到Preferences实例,每个文件唯一对应到一个Preferences实例,系统会通过静态容器把这个实例存储在内存中,直到主动从内存中移除这个实例或删除这个文件。

3.2、使用约束

1、key键为string类型,要求非空且长度不超过80个字节

2、value值为string类型时可以为空,不为空时长度不超过8192个字节

3、内存会随着存储数据量的增大而增大,所以存储的数据应该是轻量级的,建议存储的数据不要超过1万条

3.3、相关接口说明

接口大部分都是异步接口,异步接口都有callback和Promise两种返回形式。以下为callback为例说明

接口名称描述
getPreferences(context:Context,name:string,callback:AsyncCallback<Preferences>):void获取Preferences 实例
put(key:string,value:valueType,callback:AsyncCallback<void>):void把数据写入Preferences实例,可以通过flush把实例进行持 久化
has(key:string,callback:AsyncCallback<boolean>):void检查实例中是否包含指定的key的存储键值对,给定的key不可以为空
get(key:string,defValue:valueType,callback:AsyncCallback<valueType>):void获取指定键对应的值,如果值为null或者非默认值类型,返回默认数据defValue
delete(key:string,callback:AsyncCallback<void>):void从实例中删除指定key的存储键值对
flush(callback:AsyncCallback<void>):void把当前实例中的数据异步存储到用户首选项持久化文件中
on(type:'change',callback?:Callback<{key:string}>):void订阅数据变更,订阅的key的值发生变化,在执行flush方法后,触发callback回调
off(type:'change',callback?:Callback<{key:string}>):void取消订阅数据变更
deletePreferences(context:Context,name:string,callback:AsyncCallback<void>):void从内存中移除指定实例,如果这个实例有对应的持久化文件则同时会把持外化文件删除

3.4、开发步骤

1、导入用户首选项模块

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

 2、获取Preferences实例,读取指定文件,把数据加载到Preferences实例,用于数据操作

import UIAbility from '@ohos.app.ability.UIAbility';
import hilog from '@ohos.hilog';
import window from '@ohos.window';
import dataPreferences from '@ohos.data.preferences';export default class EntryAbility extends UIAbility {onCreate(want, launchParam) {}onDestroy() {}onWindowStageCreate(windowStage: window.WindowStage) {// Main window is created, set main page for this abilitytry{dataPreferences.getPreferences(this.context,'mystore',(err,preferences) => {if(err) {console.error(`Failed to get preferences. Code:${err.code},Message:${err.message}`);return;}console.info('Succeeded in getting preferences.')// 进行相关的数据操作})} catch (err) {console.error(`Failed to get preferences. Code:${err.code},Message:${err.message}`);}windowStage.loadContent('pages/Index', (err, data) => {if (err.code) {hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');return;}});}onWindowStageDestroy() {// Main window is destroyed, release UI related resources}onForeground() {// Ability has brought to foreground}onBackground() {// Ability has back to background}
}

在onWindowStageCreate方法中进行读取

3、写入数据

使用put()方法保存数据到缓存的Preferences实例中,在写入数据后如果有必要可以使用flush()方法把Preferences实例的数据存储到持久化文件。

注意:如果键此时已存在则会修改值,如果需要是在键不存在时新增键值对,则需要使用has()进行检查

4、读取数据

使用get()方法获取数据,如果值为null或者非默认值类型,则返回默认数据。

5、删除数据

使用delete()方法删除指定的键值对

6、据的持久化

应用存入数据到Preferences后,可以使用flush()方法实现数据持久化

7、订阅数据更新

使用on(),订阅key值发生变化,flush()执行时,会回调其中的回调方法

8、删除指定文件

使用deletePreferences()方法从内存中移除指定文件对应的Preferences实例,包括内存中的数据。若该Preference存在对应的持久化文件,则同时删除该持久化文件,包括指定文件及其备份文件、损坏文件。

// EntryAbility.tsimport UIAbility from '@ohos.app.ability.UIAbility';
import hilog from '@ohos.hilog';
import window from '@ohos.window';
import dataPreferences from '@ohos.data.preferences';export default class EntryAbility extends UIAbility {onCreate(want, launchParam) {}onDestroy() {}onWindowStageCreate(windowStage: window.WindowStage) {// Main window is created, set main page for this abilitytry{dataPreferences.getPreferences(this.context,'mystore',(err,preferences) => {if(err) {console.error(`Failed to get preferences. Code:${err.code},Message:${err.message}`);return;}console.info('Succeeded in getting preferences.')// 进行相关的数据操作globalThis.dataPreferences = preferences;})} catch (err) {console.error(`Failed to get preferences. Code:${err.code},Message:${err.message}`);}windowStage.loadContent('pages/Index', (err, data) => {if (err.code) {hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');return;}});}onWindowStageDestroy() {// Main window is destroyed, release UI related resources}onForeground() {// Ability has brought to foreground}onBackground() {// Ability has back to background}
}
// index.etsimport dataPreferences from '@ohos.data.preferences';
import Prompt from '@system.prompt';
import common from '@ohos.app.ability.common';@Entry
@Component
struct Index {@State message: string = '';@State message1: string = '';dpf:dataPreferences.Preferences = globalThis.dataPreferences;build() {Row() {Column() {Text(this.message).fontSize(50).fontWeight(FontWeight.Bold).margin({bottom:10})Text(this.message1).fontSize(50).fontWeight(FontWeight.Bold).margin({bottom:10})Button('put').margin({bottom:15}).onClick(() => {try{this.dpf.has('startup', (err,val) => {if(err) {console.error(`Failed to check data. Code:${err.code}, message:${err.message}`);return;}if (val) {console.info('startup is exists!!')} else {try {this.dpf.put('startup', 'auto', (err) => {if (err) {console.error(`Failed to put data. Code:${err.code}, message:${err.message}`);return;}console.info('succeeded in putting data.')})} catch (err) {console.error(`Failed to put data. Code:${err.code}, message:${err.message}`);}}})}catch(err) {console.error(`Failed to put data. Code:${err.code}, message:${err.message}`);}})Button('get').margin({bottom:15}).onClick(() => {try{this.dpf.get('startup','default',(err,val) => {if(err) {console.error(`Failed to get data. Code:${err.code}, message:${err.message}`);}console.info(`succeeded in getting value of 'startup'. val:${val} `);this.message = val.toString();})}catch (err) {console.error(`Failed to get data. Code:${err.code}, message:${err.message}`);}})Button('delete').margin({bottom:15}).onClick(() => {try{this.dpf.delete('startup', (err) => {if(err) {console.error(`Failed to delete data. Code:${err.code}, message:${err.message}`);return;}console.info('succeeded in deleting the data')})} catch (err) {console.error(`Failed to delete data. Code:${err.code}, message:${err.message}`);}})Button('flush').margin({bottom:15}).onClick(() => {try{this.dpf.flush((err) => {console.error(`Failed to flush data. Code:${err.code}, message:${err.message}`);return;})Prompt.showToast({message: '数据持久化成功!',})} catch (err) {console.error(`Failed to flush data. Code:${err.code}, message:${err.message}`);}})Button('订阅').margin({bottom:15}).onClick(() => {this.dpf.on('change',(key) => {console.info(key + '数据发生变化')})})Button('更新').margin({bottom:15}).onClick(() => {try{this.dpf.put('startup','manual',(err) => {if(err) {console.error(`Failed to put data. Code:${err.code}, message:${err.message}`);return;}})} catch (err){console.error(`Failed to put data. Code:${err.code}, message:${err.message}`);}})Button('deletePreferences').onClick(() => {try{let context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext;dataPreferences.getPreferences(context,'mystore', (err, val) => {if(err) {console.error(`Failed to delete preferences. Code:${err.code}, message:${err.message}`);return;}console.info(`successed to delete preferences.`);})} catch (err) {console.error(`Failed to delete preferences. Code:${err.code}, message:${err.message}`);}})}.width('100%')}.height('100%')}}

4、键值型数据库实现数据持久化

4.1、使用场景介绍

键值型数据库存储键值对形式的数据,一般用来存储的数据没有复杂的关系模型。

4.2、使用约束

1、设备协同数据库,针对每条记录,key的长度小于等于896Byte,value小于4MB

2、单版本数据库,针对每条记录,key的长度小于等于1KB,value小于4MB

3、每个应用程序最多支持同时打开16个键值型分布式数据库

4、键值型数据库事件回调方法不允许进行阻塞操作

4.3、相关接口说明

接口大部分是异步操作,异步接口都有callback和Promise两种形式

接口名称描述
createKVManager(config: KVManagerConfig): KVManager创建一个KVManager对象实例,用于管数据库对象
getKVStore<T>(storeId:string,options:Options,callback:AsyncCallback<T>):void指定Options和storeId,创建并得到指定类型的KVStore数据库
put(key:string,value:Uint8Array|string|number|boolean,callback:AsyncCallback<void>):void添加指定类型的键值对到数据库
get(key:string,callback:AsyncCallback<Uint8Array|string|number|boolean>):void 获取指定键对应的值
delete(key:string,callback:AsyncCallback<void>):void从数据库中删除指定键值数据

4.4、开发步骤

 1、获取一个KVManager实例

在EntryAbility.ts中onCreate()或onWindowStageCreate()中创建。

onWindowStageCreate(windowStage: window.WindowStage) {// Main window is created, set main page for this ability// KVManagerConfiglet context = this.context;const KvManagerConfig = {context: context,bundleName: 'com.xiaoxie'};try{let kvManager = distributedKVStore.createKVManager(KvManagerConfig);console.info('Succeeded in create KVManager.')globalThis.kvManager = kvManager;} catch (err){console.error(`Failed to create KVManager, Code:${err.code},Message:${err.Message}`);}windowStage.loadContent('pages/KVStore', (err, data) => {if (err.code) {hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');return;}});}

2、创建并获取键值数据库

3、调用put()方法向键值数据库中插入数据,当我们据时key值存在则会修改其值,否则新增一条数据

4、调用get()方法获取指定键值

5、调用delete()方法删除指定键值的数据

// KVStore.ets
import distributedKVStore from '@ohos.data.distributedKVStore';
import Prompt from '@system.prompt';const options = {createIfMissing: true,  // 当数据库文件不存在的时候是否创建,默认创建encrypt: false, // 设置数据库文件是否加密,默认不加密backup: false, // 设置数据库文件是否备份,默认备份kvStoreType: distributedKVStore.KVStoreType.SINGLE_VERSION, // 设置要创建数据库的类型,默认为多设备协同securityLevel: distributedKVStore.SecurityLevel.S2  // 设置数据库的安全级别
};@Entry
@Component
struct KVStore {@State message: string = '';kvManager:distributedKVStore.KVManager = globalThis.kvManagerkvStore:distributedKVStore.SingleKVStore = undefined;build() {Row() {Column() {Text(this.message).fontSize(20).fontWeight(FontWeight.Bold).margin({bottom:10})Button('1、创建并获取键值数据库').width('50%').margin({bottom:10}).onClick(() => {try{this.kvManager.getKVStore('storeId',options,(err,kvStore:distributedKVStore.SingleKVStore) => {if(err) {console.error(`Failed to get KVStore, Code:${err.code},Message:${err.message}`);return;}Prompt.showToast({message: '获取键值数据库成功!',duration: 1500})this.kvStore = kvStore;});} catch (e) {console.error(`Failed to get KVStore, Code:${e.code},Message:${e.message}`);}})Button('2、向键值数据库插入数据').width('50%').margin({bottom: 10}).onClick(() => {try{if(this.kvStore) {this.kvStore.put('test_key','test_value',(err) => {if(err) {console.error(`Failed to put data, Code:${err.code},Message:${err.message}`);return;}Prompt.showToast({message: '向键值数据加中插入数据成功!',duration: 1500})})} else {Prompt.showToast({message: '错误:请先获取数据库!',duration: 1500})}} catch (e) {console.error(`Failed to put data, Code:${e.code},Message:${e.message}`);}})Button('3、获取指定键的值').width('50%').margin({bottom:10}).onClick(() => {try{if(this.kvStore) {this.kvStore.get('test_key',(err,data) => {if(err) {this.message = '';console.error(`Failed to get data, Code:${err.code},Message:${err.message}`);return;}this.message = '';this.message = data.toString();})} else {Prompt.showToast({message: '错误:请先获取数据库!',duration: 1500})}} catch (e) {console.error(`Failed to get data, Code:${e.code},Message:${e.message}`);}})Button('4、删除指定键值数据').width('50%').onClick(() => {try{if(this.kvStore) {this.kvStore.delete('test_key',(err) => {if(err) {console.error(`Failed to delete data, Code:${err.code},Message:${err.message}`);return;}Prompt.showToast({message: '删除指定键值数据成功!',duration: 1500})})} else {Prompt.showToast({message: '错误:请先获取数据库!',duration: 1500})}} catch (e){console.error(`Failed to delete data, Code:${e.code},Message:${e.message}`);}})}.width('100%')}.height('100%')}
}

5、关系型数据库实现数据持久化

5.1、使用场景介绍

关系型数据库基于SQLite组件,适用于存储包含复杂关系数据的场景。

5.2、相关术语介绍

1、谓词:数据库中用来代表数据实体的性质、特征或数据实体之间关系的词项,它主要用来定义数据库的操作条件。

2、结果集:指的是用户查询后的结果集合。

5.3、动作机制

关系型数据库对应用提供通用的操作接口,底层使用SQLite作为持久化存储引擎。

5.4、约束限制

1、系统默认日志方式是WAL(Write Ahead Log)模式,默认落盘方式是FULL模式

2、连接池最大个数是4个

3、数据库同一时间只能支持一个写操作

4、当应用被卸载完成后,设备上的相关数据库文件及临时文件会被自动清除

5.5、相关接口说明

大部分为异步接口,异步接口都有callback和Promise两种形式

接口说明描述
getRdbStore(context: Context, config: StoreConfig, callback:AsyncCallback<RdbStore>): void获得一个RdbStore,操作关系型数据库,用户可以根据自己的需求配置RdbStore的参数,然后通过RdbStore调用相关接口来执行相关的数据操作
executeSql(sql: string, bindArgs:Array<ValueType>,callback:AsyncCallback<void>): void执行包含指定参数但是不返回值的sql语句
inser(table:string,values:valuesBucket,callback:AsyncCallback<number>): void向表中插入一行数据
update(values:ValuesBucket,predicates:RdbPredicates,callback:AsyncCallback<number>): void根据RdbPredicates的指定实例对象更新数据库中数据
delete(predicates:RdbPredicates,callback:AsyncCallback<number>):void根据RdbPredicates的指定实例对象删除数据库中数据
query(predicates:RdbPredicates,columns:Array<string>,callback:AsyncCallback<ResultSet>):void根据指定条件查询数据库中的数据
deleteRdbStore(context:Context,name:string,callback:AsyncCallback<void>):void删除数据库

5.6、开发步骤

 1、获取一个RdbStore

在EntryAbility.ts文件件的onCreate()或onWindowStageCreate()方法中创建数据库获取到RdbStore对象并绑定到globalThis,便于后续使用。

// EntryAbility.ts
onWindowStageCreate(windowStage: window.WindowStage) {// RdbStoreconst STORE_CONFIG = {name: 'RdbTest.db', // 数据库名称securityLevel: relationalStore.SecurityLevel.S1 // 数据库的安全级别};relationalStore.getRdbStore(this.context, STORE_CONFIG, (err,store) => {if(err) {console.error(`Failed to create rdbstore. Code:${err.code},Message:${err.message}`);return;}console.info('Succeeded in create RdbStore.')globalThis.rdbStore = store;})
}

2、创建数据表,定义好建表语句,执行executeSql()方法

3、调用insert()方法来插入数据

注意:关系型数据库没有显式的flush操作实现持久化,数据插入即保存在持久化文件

4、根据谓词指定的实例对象,对数据进行修改或删除

5、根据谓词指定的查询条件查找数据,调用query()方法查找数据,会返回一个ResultSet结果集,当应用完成查询数据操作,不再使用ResultSet时,及时调用close方法关闭结查集,释放内存空间。

5、删除数据库,调用deleteRdbStore()方法,用来删除数据库及数据库相关的文件

import relationalStore from '@ohos.data.relationalStore'
import Prompt from '@system.prompt';
import common from '@ohos.app.ability.common';@Entry
@Component
struct RdbStore {@State message: string = ''rdbStore: relationalStore.RdbStore = globalThis.rdbStore;build() {Row() {Column() {Text(this.message).fontSize(20).fontWeight(FontWeight.Bold).margin({bottom:10})Button('创建数据表').width('50%').margin({bottom:10}).onClick(() => {try {// 数据库建表语句let SQL_CREATE_TABLE: string = 'create table if not exists employee(id integer primary key autoincrement, name text not null, age integer, salary real, codes blob)';this.rdbStore.executeSql(SQL_CREATE_TABLE); // 执行sql语句,创建数据表Prompt.showToast({message: '创建数据表成功',duration: 1500})} catch (err) {console.error(`创建数据表失败,Code:${err.code},Message:${err.Message}`);}})Button('向数据表中插入数据').width('50%').margin({bottom:10}).onClick(() => {let valueBucket = {'name': '张三','age': 18,'salary': 20000.00,'codes' : new Uint8Array([1,2,3,4,5])};this.rdbStore.insert('employee',valueBucket,(err,rowId) => {if(err) {console.error(`Failed to insert data. Code:${err.code},Message:${err.message}}`);return;}Prompt.showToast({message: `插入数据成功,插入数据id:${rowId}`,duration: 2000})})})Button('修改数据表中指定数据').width('50%').margin({bottom:10}).onClick(() => {let valueBucket = {'name': '李四','age': 18,'salary': 20000.00,'codes' : new Uint8Array([1,2,3,4,5])};let predicates = new relationalStore.RdbPredicates('employee'); // 创建表employee的predicatespredicates.equalTo('name','张三');  // 匹配employee中name为张三的字段this.rdbStore.update(valueBucket,predicates,(err,rows) => {if(err) {console.error(`Failed to update data. Code:${err.code},Message:${err.message}}`);return;}Prompt.showToast({message: `修改数据成功,修改影响记录行数:${rows}`,duration: 2000})})})Button('删除数据表中指定数据').width('50%').margin({bottom:10}).onClick(() => {let predicates = new relationalStore.RdbPredicates('employee');predicates.equalTo('name','李四');this.rdbStore.delete(predicates, (err,rows) => {if(err) {console.error(`Failed to delete data. Code:${err.code},Message:${err.message}}`);return;}Prompt.showToast({message: `删除数据成功,删除影响记录行数:${rows}`,duration: 2000})})})Button('查询数据').width('50%').margin({bottom:10}).onClick(() => {this.message = '';let predicates = new relationalStore.RdbPredicates('employee');predicates.equalTo('name','张三');this.rdbStore.query(predicates,['id','name','age','salary','codes'],(err,resultSet) => {if(err) {this.message = '';console.error(`Failed to query data. Code:${err.code}, message:${err.message}`);return;}// this.message = resultSet.columnNames.join('|')if(resultSet.rowCount > 0 ){resultSet.goToFirstRow();this.message = resultSet.getString(resultSet.getColumnIndex('name')) + ' - ' + resultSet.getDouble(resultSet.getColumnIndex('salary'));}resultSet.close();})})Button('删除数据库及相关文件').width('50%').onClick(() => {let context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext;relationalStore.deleteRdbStore(context,'RdbTest.db',(err) => {if(err) {console.error(`Failed to delete RdbStore. Code:${err.code}, message:${err.message}`);return;}Prompt.showToast({message: '删除数据库成功!',duration: 1500})})})}.width('100%')}.height('100%')}
}

6、数据可靠性与安全性

6.1、功能场景说明

在系统运行中,存储损坏、存储空间不足、文件系统权限、系统掉电等等都可能导致数据库发生故障。为此数据管理提供了数据可靠与安全性相关的解决方案和保障。

  • 备份、恢复功能:重要业务数据丢失出息严重异常场景,可以通过备份恢复数据库,保障数据不丢失
  • 数据库加密功能:当数据库中存储一些敏感信息时,可以对数据库进行加密,提高数据的安全性
  • 数据库分类分级:提供基于数据安全标签和设备安全等级进行访问控制的能力,保证数据安

另: 备份数据库存储在应用的沙箱中,当存储空间不足时,可以选择删除本地的数据库备份,释放空间。

6.2、基本概念

6.2.1、数据库备份与恢复

  • 数据库备份:对当前数据库的数据库文件进行完整备份,在做备份时不用关闭数据库,直接调用对应的备份接口就可以了
  • 数据库恢复:从指定的备份文件恢复到当前数据库文件。恢复完后,当前数据库数据保持与指定备份文件一致

6.2.2、数据库加密

加密是对整个数据库文件的加密,可以增强数据库的安全性,有效保护数据库内容

6.2.3、数据库分类分级

分布式数据管理对数据实施分类分级保护,提供基于数据安全标签以及设备安全等级的访问控制机制。数据安全标签和设备安全等级越高,加密措施和访问控制措施越严格,数据安全性越高。

6.3、运作机制

6.3.1、数据库备份与恢复机制

数据库在备份时,会把当前数据库备份在指定的文件件中,后续对数据库的操作不会影响备份的数据库文件,只有当恢复指定的数据库文件时,才会把备份的数据库文件件覆盖当前数据库,实现回滚。

键值型数据库备份路径:/data/service/el1(el2)/public/database/...{appId}/kvdb/backup/...{storeId}

关系型数据库备份路径:/data/app/el1(el2)/100/database/...{bundlename}/rdb

6.3.2、数据库加密机制

在数据库加密时,开发者无需传入密钥,只需要设置数据库加密的状态即可。系统会自动帮助开发者把数据库加密。使用huks通用密钥库系统。

6.4、相关约束限制

数据库加密密钥一年自动更换一次

键值型数据库最多可以备份5份

键值型数据库的自动备份需要在熄屏且充电的状态下进行

6.5、数据库备份与恢复

键值型数据库和关系型数据库都支持数据库的备份与恢复。同时键值型数据库还可以删除数据库备份,以释放本地存储空间。

6.5.1、键值型数据库的备份、恢复及删除

键值型数据库,通过backup接口实现数据库备份,通过restore接口实现数据库恢复,通过deletebackup接口删除数据库备份。


import distributedKVStore from '@ohos.data.distributedKVStore';
import Prompt from '@system.prompt';
const options = {createIfMissing: true,encrypt: false,backup: false,kvStoreType: distributedKVStore.KVStoreType.SINGLE_VERSION,securityLevel: distributedKVStore.SecurityLevel.S2
};@Entry
@Component
struct KVStoreBack {@State message: string = '';kvManager: distributedKVStore.KVManager = globalThis.kvManager;kvStore: distributedKVStore.SingleKVStore;build() {Row() {Column() {Text(this.message).fontSize(20).fontWeight(FontWeight.Bold).margin({bottom:10})Button('1.创建并获取键值数据库').width('50%').margin({bottom:10}).onClick(() => {let kvStorePromise = this.kvManager.getKVStore("myStore",options);kvStorePromise.then((store: distributedKVStore.SingleKVStore) => {this.kvStore = store;}).catch((err) => {console.error(`创建并获取键值数据库失败, 失败代码:${err.code}, 失败原因:${err.message}`);return;})})Button('2.键值数据库插入数据').width('50%').margin({bottom:10}).onClick(() => {try {this.kvStore.put('name', 'xiaoxie', (err) => {if (err) {console.error(`存储数据失败, 失败代码:${err.code}, 失败原因:${err.message}`);return;}Prompt.showToast({message: '存储数据成功!!',duration: 1500})this.message = 'xiaoxie'; // 添加数据成功把message赋值为写入的值})} catch (e) {console.error(`存储数据失败, 失败代码:${e.code}, 失败原因:${e.message}`);}})Button('3.备份数据').width('50%').margin({bottom:10}).onClick(() => {try{let file = 'bk001';this.kvStore.backup(file, (err) => {if(err) {console.error(`备份数据失败, 失败代码:${err.code}, 失败原因:${err.message}`);} else {Prompt.showToast({message: `备份数据成功,备份文件:${file}`,duration: 1500})}})} catch (e) {console.error(`备份数据失败, 失败代码:${e.code}, 失败原因:${e.message}`);}})Button('4.删除数据').width('50%').margin({bottom:10}).onClick(() => {try{this.kvStore.delete('name',(err) => {if(err) {console.error(`删除数据失败, 失败代码:${err.code}, 失败原因:${err.message}`);return;}Prompt.showToast({message: '删除数据成功!',duration: 1500})this.message = ''; // 删除数据成功后把message置空})} catch (e) {console.error(`删除数据失败, 失败代码:${e.code}, 失败原因:${e.message}`);}})Button('5.恢复数据').width('50%').margin({bottom:10}).onClick(() => {this.message = ''try{this.kvStore.restore('bk001',(err) => {if(err) {console.error(`删除数据失败, 失败代码:${err.code}, 失败原因:${err.message}`);} else {try{this.kvStore.get('name',(err,data) => {if(err) {console.error(`获取数据失败, 失败代码:${err.code}, 失败原因:${err.message}`);return;}this.message = data.toString(); // 恢复数据后把message的值赋为恢复后的值})} catch (e) {console.error(`获取数据失败, 失败代码:${err.code}, 失败原因:${err.message}`);}}})} catch (e) {console.error(`恢复数据失败, 失败代码:${e.code}, 失败原因:${e.message}`);}})Button('6、删除备份').width('50%').onClick(() => {let files = ['bk001'];try{this.kvStore.deleteBatch(files).then((data) => {Prompt.showToast({message: `删除备份成功,文件:${data[0]},结果:${data[1]}`,})}).catch((err) => {console.error(`删除备份失败, 失败代码:${err.code}, 失败原因:${err.message}`);})} catch (e) {console.error(`删除备份失败, 失败代码:${e.code}, 失败原因:${e.message}`);}})}.width('100%')}.height('100%')}
}

6.5.2、关系型数据库备份与恢复

关系型数据库,通过backup接口实现数据库备份,通过restore接口实现数据库恢复。

import relationalStore from '@ohos.data.relationalStore';
import Prompt from '@system.prompt';
const SQL_CREATE_TABLE: string = 'create table if not exists employee(id integer primary key autoincrement, name text not null, age integer, salary real, codes blob)';@Entry
@Component
struct RdbStroeBack {@State message: string = '';rdbStore: relationalStore.RdbStore = globalThis.rdbStore;build() {Row() {Column() {Text(this.message).fontSize(20).fontWeight(FontWeight.Bold).margin({bottom:10})Button('1.创建数据表').width('50%').margin({bottom:10}).onClick(() => {try{this.rdbStore.executeSql(SQL_CREATE_TABLE);Prompt.showToast({message:'创建数据表成功',duration:1500})} catch (e){console.error(`创建数据表失败!Code:${e.code},Message:${e.message}`);}})Button('2.向数据表插入数据').margin({bottom:10}).width('50%').onClick(() => {let valueBucket = {'name': '赵子龙','age': 20,'salary': 100.5,'codes': new Uint8Array([1,2,3,4,5])};this.rdbStore.insert('employee',valueBucket).then((rowId) => {Prompt.showToast({message: '插入数据成功!',duration: 1500})this.message = `${rowId} - ${valueBucket.name} - ${valueBucket.salary}`}).catch((err) => {console.error(`插入数据失败!Code:${err.code},Message:${err.message}`);})})Button('3.备份数据库').margin({bottom:10}).width('50%').onClick(() => {this.rdbStore.backup('dbBackup.db',(err) => {if(err) {console.error(`备份数据库失败!Code:${err.code},Message:${err.message}`);return;}Prompt.showToast({message: '备份数据库成功!',duration: 1500})})})Button('4.删除数据').width('50%').margin({bottom:10}).onClick(() => {let predicates = new relationalStore.RdbPredicates('employee');predicates.equalTo('name','赵子龙');this.rdbStore.delete(predicates).then((rows) => {if(rows > 0) {this.message = '';  // 删除后清空界面展示信息}}).catch((e) => {console.error(`删除指定数据失败!Code:${e.code},Message:${e.message}`);})})Button('5.恢复数据').width('50%').onClick(() => {this.rdbStore.restore('dbBackup.db',(err) => {if(err) {console.error(`恢复数据失败. Code:${err.code},message:${err.message}`);} else {let predicates = new relationalStore.RdbPredicates('employee');predicates.equalTo('name','赵子龙');this.rdbStore.query(predicates,['id','name','salary'],(err,resultSet) => {if(err) {console.error(`查询数据失败. Code:${err.code},message:${err.message}`);} else {if(resultSet.rowCount > 0) {resultSet.goToFirstRow();this.message = resultSet.getLong(resultSet.getColumnIndex('id')) + ' - '+ resultSet.getString(resultSet.getColumnIndex('name')) + ' - '+ resultSet.getDouble(resultSet.getColumnIndex('salary'));}}})}})})}.width('100%')}.height('100%')}
}

6.6、数据库加密

6.6.1、使用场景

为了增强数据库的安全性,数据库提供了一个安全适用的数据库加密能力,从而对数据库存储的内容实施有效保护。通过数据库加密等安全方法实现了数据库数据存储的保密性和完整性要求,使得数据库以密文方式存储并在密态方式下工作,确保了数据安全。

注意:加密后的数据库只能通过接口进行访问,无法通过其它的方式来打开数据库文件。数据库加密的属性在创建数据库时确认,无法变更。

键值型数据库和关系型数据库都直接数据加密操作

6.6.2、键值型数据库加密

通过options中encrypt参数来控制是否加密,默认是false,表示不加密,当这个参数为true时则表示加密处理。

import distributedKVStore from '@ohos.data.distributedKVStore'
import Prompt from '@system.prompt';const options = {createIfMissing: true,encrypt: true, // 数据库加密backup: false,kvStoreType: distributedKVStore.KVStoreType.SINGLE_VERSION,securityLevel: distributedKVStore.SecurityLevel.S2
};@Entry
@Component
struct KVStoreEncrypt {@State message: string = ''kvManager:distributedKVStore.KVManager = globalThis.kvManager;kvStore: distributedKVStore.SingleKVStore;build() {Row() {Column() {Text(this.message).fontSize(20).fontWeight(FontWeight.Bold).margin({bottom:10})Button('创建加密数据库').width('50%').margin({bottom:10}).onClick(() => {this.kvManager.getKVStore('ms1',options,(err,store:distributedKVStore.SingleKVStore) => {if(err) {console.error(`创建加密数据库失败!,代码:${err.code},原因:${err.message}`);return;}this.kvStore = store;Prompt.showToast({message: `创建加密数据库成功!`,duration: 2000})})})}.width('100%')}.height('100%')}
}

其中kvManager对象还是在EntryAbility.ts的onCreate或onWindowStageCreate中创建的

    // KVManagerConfiglet context = this.context;const KvManagerConfig = {context: context,bundleName: 'com.xiaoxie'};try{let kvManager = distributedKVStore.createKVManager(KvManagerConfig);console.info('Succeeded in create KVManager.')globalThis.kvManager = kvManager;} catch (err){console.error(`Failed to create KVManager, Code:${err.code},Message:${err.Message}`);}

6.6.3、关系型数据库加密

关系型数据库,过在创建relationalStore.RdbStore时给定的config参数中指定encrypt:true即可。

这个参数默认是false的

    // RdbStoreconst STORE_CONFIG = {name: 'RdbTest.db', // 数据库名称securityLevel: relationalStore.SecurityLevel.S1 // 数据库的安全级别};relationalStore.getRdbStore(this.context, STORE_CONFIG, (err,store) => {if(err) {console.error(`Failed to create rdbstore. Code:${err.code},Message:${err.message}`);return;}console.info('Succeeded in create RdbStore.')globalThis.rdbStore = store;})

相关文章:

HarmonyOS开发(九):数据管理

1、概述 1.1、功能简介 数据管理为开发者提供数据存储、数据管理能力。 它分为两个部分&#xff1a; 数据存储&#xff1a;提供通用数据持久化能力&#xff0c;根据数据特点&#xff0c;分为用户首选项、键值型数据库和关系型数据库。数据管理&#xff1a;提供高效的数据管…...

acwing-Linux学习笔记

acwing-Linux课上的笔记 acwing-Linux网址 文章目录 1.1常用文件管理命令homework作业测评命令 2.1 简单的介绍tmux与vimvimhomeworktmux教程vim教程homework中的一些操作 3 shell语法概论注释变量默认变量数组expr命令read命令echo命令printf命令test命令与判断符号[]逻辑运算…...

Python渗透测试——一、数据包的编辑工具——Scapy

Python渗透测试 一、Scapy简介二、Scapy中的分层结构三、Scapy中的常用函数四、在Scapy 中发送和接收数据包五、Scapy 中的抓包函数 一、Scapy简介 提到数据包(这里泛指帧、段和报文等)的构造&#xff0c;我们首先需要了解协议和分层这两个概念。在“互联世界的规则一协议”中…...

使用webstrom编写vue开启提示

1.语言服务器选择 2.文件类型–忽略的文件和文件夹&#xff0c;删去&#xff0c;node_modules&#xff0c;就可以点进去库了 3.禁用JSLint、TSLint 4.开启node辅助 5.如果是vite&#xff0c;开启自动读取&#xff0c;或手动指定 6.如果是Webpack&#xff0c;开启自动读取&#…...

linux远程桌面管理工具(xrdp)、向日葵

Windows远程桌面 linux远程桌面 使用向日葵远程桌面&#xff08;手机端同理&#xff09; Windows远程桌面 微软自带Remote Desktop Connection Manager &#xff08;RDCMan&#xff09;远程控制管理软件介绍 远程桌面连接管理器 v2.93 linux远程桌面 Windows远程桌面Ubunt…...

【力扣100】8.找到字符串中所有字母异位词

添加链接描述 class Solution:def findAnagrams(self, s: str, p: str) -> List[int]:sildingstrresult[]p.join(sorted(p))for i in range(len(s)):if len(sildingstr)<len(p):sildingstrsildingstrs[i]# print(sildingstr)if len(sildingstr)len(p):sort_sildingstr.j…...

圆通速递查询,圆通速递单号查询,用表格导出查询好的物流信息

批量查询圆通速递单号的物流信息&#xff0c;以表格的形式导出查询好的物流信息。 所需工具&#xff1a; 一个【快递批量查询高手】软件 圆通速递单号若干 操作步骤&#xff1a; 步骤1&#xff1a;运行【快递批量查询高手】软件&#xff0c;并登录 步骤2&#xff1a;点击主界…...

FLStudio中文2024中文最新汉化安装包下载

FLStudio中文21最新版本以其使用速度而闻名&#xff0c;是一个高度复杂的音乐制作环境。FL Studio免费&#xff0c;联合国音序器音频和MIDI每个复合编辑都是音乐。现代的DAW是一种非凡的野兽。首先&#xff0c;它在很大程度上把自己放在了(几乎)每个人记录过程的核心。其次&…...

AI:大语言模型训练方法 - 机器学习

Transformer Transformer是一种深度学习的模型架构&#xff0c;特别适用于自然语言处理任务。Transformer 模型的核心创新在于其 "自注意力"&#xff08;Self-Attention&#xff09;机制&#xff0c;这种机制使得模型可以有效地捕捉输入数据中的长距离依赖关系。 T…...

Linux(17):认识与分析登录档

什么是登录档 【详细而确实的分析以及备份系统的登录文件】是一个系统管理员应该要进行的任务之一。 登录档 就是记录系统活动信息的几个文件&#xff0c;例如&#xff1a;何时、何地(来源IP)、何人(什么服务名称)、做了什么动作(讯息登录啰)。 换句话说就是&#xff1a;记录系…...

STM32上模拟CH340芯片的功能 (一)

#虚拟串口模拟CH340# 代码gitee地址&#xff1a;STM32F103_CH340: 用STM32模拟ch340USB串口的功能 一、思路 1. 确定通信接口&#xff1a;CH340是一款USB转串口芯片&#xff0c;因此您需要选择STM32上的某个USB接口来实现USB通信。通常情况下&#xff0c;STM32系列芯片都有内…...

图论——最小生成树

图论——最小生成树 A wise man changes his mind, a fool never will 生成树 一个连通图的生成树是一个极小的连通子图&#xff0c;它包含图中全部的n个顶点&#xff0c;但只有构成一棵树的n-1条边。 最小生成树 在这些边中选择N-1条出来&#xff0c;连接所有的N个点。这N-1…...

C++基础 -42- STL库之list链表

———————STL库之list链表——————— &#x1f384; list链表的格式(需要定义头文件) list<int> data1(4, 100);list<int> data2(4, 500);&#x1f384;list链表的合并接口 &#x1f384;举例使用合并接口并且验证 data2.merge(data1);list<int>::…...

Backend - Python 序列化

目录 一、作用1&#xff1a;代码块存入数据库 二、作用2&#xff1a;前后端传递数据 &#xff08;一&#xff09;前端 1. JSON.stringify() 2. JSON.parse() &#xff08;二&#xff09;后端 1. json.dumps() &#xff08;1&#xff09;作用 &#xff08;2&#xff09…...

初级数据结构(一)——顺序表

文中代码源文件已上传&#xff1a;数据结构源码 <-上一篇 NULL | 初级数据结构&#xff08;二&#xff09;——链表 下一篇-> 1、顺序表的特点 1.1、数组 现实中数据记录一般都记录在表格中&#xff0c;如进货单、菜单等&#xff0c;它们的最大特点就是…...

实现:切换页面切换标题,扩展 vue-router 的类型

布局容器-页面标题 网址&#xff1a;https://router.vuejs.org/zh/guide/advanced/meta 给每一个路由添加 元信息 数据 router/index.ts const router createRouter({history: createWebHistory(import.meta.env.BASE_URL),routes: [{ path: /login, component: () > im…...

已通过考试和认证注册以及后续计划表

已通过考试和认证注册以及后续计划表 软考 - 计算机技术与软件专业技术资格&#xff08;水平&#xff09;考试信息系统集成及服务项目管理人员工程类考试计划你关注的证书样子 软考 - 计算机技术与软件专业技术资格&#xff08;水平&#xff09;考试 高级 信息系统项目管理师&…...

开源计算机视觉库OpenCV详解

目录 1、概述 2、OpenCV详细介绍 2.1、OpenCV的起源 2.2、OpenCV开发语言 2.3、OpenCV的应用领域 3、OpenCV模块划分 4、OpenCV源码文件结构 4.1、根目录介绍 4.2、常用模块介绍 4.3、CUDA加速模块 5、OpenCV配置以及Visual Studio使用OpenCV 6、关于Lena图片 7、…...

使用pytorch查看中间层特征矩阵以及卷积核参数

这篇是我对哔哩哔哩up主 霹雳吧啦Wz 的视频的文字版学习笔记 感谢他对知识的分享 1和4是之前讲过的alexnet和resnet模型 2是分析中间层特征矩阵的脚本 3是查看卷积核参数的脚本 1设置预处理方法 和图像训练的时候用的预处理方法保持一致 2实例化模型 3载入之前的模型参数 4载入…...

HarmonyOS4.0从零开始的开发教程09页签切换

HarmonyOS&#xff08;七&#xff09;页签切换 List组件和Grid组件的使用 Tabs组件的使用 概述 在我们常用的应用中&#xff0c;经常会有视图内容切换的场景&#xff0c;来展示更加丰富的内容。比如下面这个页面&#xff0c;点击底部的页签的选项&#xff0c;可以实现“首页…...

大电流H桥电机驱动电路的设计与解析(包括自举电路的讲解,以IR2104+LR7843为例)

大电流H桥电机驱动电路的设计与解析&#xff08;包括自举电路的讲解&#xff0c;以IR2104LR7843为例&#xff09; 电机驱动板主要采用两种驱动芯片&#xff0c;一种是全桥驱动&#xff08;如&#xff1a;HIP4082&#xff09;&#xff0c;一种是半桥驱动&#xff08;如&#xff…...

windows11 windows 11 (win11 win 11) 怎么安装 Python3 ? numpy? sounddevice? 声音信号处理库?

首先确认要安装的 sounddevice 库&#xff0c;链接&#xff1a;https://python-sounddevice.readthedocs.io/en/0.4.6/ 根据文档&#xff0c;可知最新的 sounddevice 版本是 0.4.6 进入安装页面查看&#xff0c;发现 Newest sounddevice 可以使用 pip 安装&#xff0c;如下图…...

git如何配置多个远程仓库,并且进行切换

一、配置多个远程仓库并进行切换&#xff0c;请按照以下步骤进行操作&#xff1a; 打开命令行终端&#xff0c;并进入您的 Git 仓库所在的目录。添加第一个远程仓库&#xff0c;使用以下命令&#xff1a;git remote add origin <第一个远程仓库的 URL>这里将远程仓库命名…...

计算机存储单位 + 程序编译过程

C语言的编译过程 计算机存储单位 头文件包含的两种方式 使用 C/C 程序常用的IDE 常用的C语言编译器&#xff1a; 在选择编译器时&#xff0c;需考虑平台兼容性、性能优化、调试工具和开发人员的个人偏好等因素。 详细教程可转 爱编程的大丙...

vue路由导航守卫(全局守卫、路由独享守卫、组件内守卫)

目录 一、什么是Vue路由导航守卫&#xff1f; 二、全局守卫 1、beforeEach 下面是一个beforeEach的示例代码&#xff1a; 2、beforeResolve 下面是一个beforeResolve的示例代码&#xff1a; 3、afterEach 下面是一个afterEach的示例代码&#xff1a; 三、路由独享守卫…...

单片机双机通信控制跑马灯

实验要求 两个单片机各驱动8个LED灯&#xff0c;构成两个跑马灯&#xff0c;要求甲单片机LED的点亮方式是从上至下&#xff0c;首先是最上面第一个点亮、其次是前两个点亮、其次是前三个点亮……直至8个灯全部点亮&#xff0c;8个灯全部灭&#xff0c;重复这个过程&#xff0c…...

微信小程序:button微信开放能力打开客服会话分享到聊天框

文档 https://developers.weixin.qq.com/miniprogram/dev/component/button.html 打开客服会话 按钮关键属性 open-type"contact"功能按钮 <button class"mo-open-type"open-type"contact"> </button>分享 <button class&q…...

【数据结构】——队列实现二叉树的功能

前言&#xff1a;二叉树的实现方式多种多样&#xff0c;有数组实现满二叉树&#xff0c;有链表实现完全二叉树&#xff0c;今天我们就用队列来实现二叉树。 创建二叉树&#xff1a; typedef int BTDataType; typedef struct BinaryTreeNode {BTDataType data;struct BinaryTre…...

【已解决】Win7虚拟机安装VMtools报错

在做以前的实验的时候发现要用到Win7虚拟机&#xff0c;于是就安装了一个Win7的虚拟机&#xff0c;但是发现屏幕太小&#xff0c;而且来回复制文本、复制文件太不方便了&#xff0c;索性就安装了VMtools&#xff0c;发现还安装不成– 情况1 报错&#xff1a;本程序需要您将此…...

华为OD机试真题-小明找位置-2023年OD统一考试(C卷)

题目描述&#xff1a; 小朋友出操&#xff0c;按学号从小到大排成一列&#xff1b;小明来迟了&#xff0c;请你给小明出个主意&#xff0c;让他尽快找到他应该排的位置。 算法复杂度要求不高于nLog(n)&#xff1b;学号为整数类型&#xff0c;队列规模<10000&#xff1b; 输…...

2023.2版idea安装教程,现在jdk8已经过去式了,不同idea支持的jdk不同。升级jdk后idea也要随之升级

下载idea2023.2版本&#xff0c;下载之前需要删除之前的版本&#xff0c;一定要删除干净&#xff0c;删除程序要勾选那两个delete 下载路径&#xff1a;其他版本 - IntelliJ IDEA (jetbrains.com.cn) 选择2023.2版本 下载后进入安装程序&#xff0c;选择安装目录&#xff0c;然…...

CSS3技巧36:让内容垂直居中的三种方式

让内容垂直居中&#xff0c;是一个很重要的应用情景&#xff0c;在很多场合都会需要。这也是面试的时候&#xff0c;一些考官喜欢拿来初面的小题目。 这里&#xff0c;小结下让内容垂直居中的三种方式。 当然&#xff0c;读者如果有更好的方法&#xff0c;也可以提出来。 基本…...

空间运算设备-Apple Vision Pro

苹果以其在科技领域的创新而闻名&#xff0c;他们致力于推动技术的边界&#xff0c;这在他们的产品中表现得非常明显。他们尝试开发一项的新型突破性显示技术。在 2023 年 6 月 5 日官网宣布将发布 Apple Vision Pro 头戴空间设备&#xff0c;我们一起来了解一下 Apple Vision …...

cocos creator “TypeError: Cannot set property ‘string‘ of null

背景&#xff1a; 学习cocos creator时遇到"TypeError: Cannot set property string of null" 错误。具体代码如下&#xff1a;property({ type: Label })public stepsLabel: Label | null null;update(deltaTime: number) {this.stepsLabel.string Math.floor(…...

简谈MySQL的binlog模式

一、MySQL的binlog模式介绍 MySQL的binlog模式是一种日志模式&#xff0c;用于记录对MySQL数据库进行的更改操作。通过启用binlog模式&#xff0c;可以将数据库的更改操作记录到二进制日志文件中&#xff0c;以便在后续需要时进行恢复和复制。 要启用binlog模式&#xff0c;请…...

Linux 环境部署RabbitMQ

1.单机部署 我们在Centos7虚拟机中使用Docker来安装。 1.1.下载镜像 方式一&#xff1a;在线拉取 docker pull rabbitmq:3-management 方式二&#xff1a;从本地加载&#xff08;本文章带有mq安装包&#xff09; docker load -i mq.tar 1.2.安装MQ 执行下面的命令来运行…...

【1day】泛微e-office OA系统xml.php 文件 SORT_ID 参数 SQL 注入漏洞学习

注:该文章来自作者日常学习笔记,请勿利用文章内的相关技术从事非法测试,如因此产生的一切不良后果与作者无关。 目录 一、漏洞描述 二、影响版本 三、资产测绘 四、漏洞复现...

智能无人零售:革新零售消费体验的未来

智能无人零售&#xff1a;革新零售消费体验的未来 在当今数字化时代&#xff0c;智能无人零售正以惊人的速度改变着我们的购物方式和消费体验。这一新兴领域的发展&#xff0c;为消费者带来了前所未有的便利和个性化选择。 智能无人零售是指利用先进的智能技术和自动化系统&…...

代币化对网约车区块链平台的影响

The effects of tokenization on ride-hailing blockchain platforms 再一次分析一下一篇关于区块链的文章&#xff0c;这篇文章比较新&#xff0c;2023年发表在POMS上。 由于这篇文章跟之前那几篇关注假货的文章的重点不一样&#xff0c;所以需要仔细读一下他的INTRODUCTION…...

YOLOv7 学习笔记

文章目录 前言一、YOLOv7贡献和改进二、YOLOv7核心概念三、YOLOv7架构改进总结 前言 在深度学习和计算机视觉领域&#xff0c;目标检测一直是一个极具挑战性和实用性的研究领域。特别是在实时目标检测方面&#xff0c;准确率和速度之间的平衡成为了关键考量因素。YOLO&#xf…...

【51单片机系列】74HC595实现对LED点阵的控制

本文是关于LED点阵的使用&#xff0c;使用74HC595模块实现对LED点阵的控制。 文章目录 一、8x8LED点阵的原理1.1 LED点阵显示原理1.2 LED点阵内部结构图1.3 开发板上的LED点阵原理图1.4 74HC595芯片 二、使用74HC595模块实现流水灯效果三、 使用74HC595模块控制LED点阵对角线亮…...

Canal笔记:安装与整合Springboot模式Mysql同步Redis

官方文档 https://github.com/alibaba/canal 使用场景 学习一件东西前&#xff0c;要知道为什么使用它。 1、同步mysql数据到redis 常规情况下&#xff0c;产生数据的方法可能有很多地方&#xff0c;那么就需要在多个地方中&#xff0c;都去做mysql数据同步到redis的处理&…...

C++的继承语法

在面向对象编程中&#xff0c;继承是一种强大的机制&#xff0c;允许一个类&#xff08;子类&#xff09;从另一个类&#xff08;父类&#xff09;继承属性和方法。C是一种支持面向对象编程的编程语言&#xff0c;通过其灵活而强大的继承语法&#xff0c;开发者可以构建更加模块…...

C# .NET平台提取PDF表格数据,并转换为txt、CSV和Excel表格文件

处理PDF文件中的内容是比较麻烦的事情&#xff0c;特别是以表格形式呈现的各种数据。为了充分利用这些宝贵的数据资源&#xff0c;我们可以通过程序提取PDF文件中的表格&#xff0c;并将其保存为更易于处理和分析的格式&#xff0c;如txt、csv、xlsx&#xff0c;从而更方便地对…...

spring boot学习第五篇:spring boot与JPA结合

1、准备表&#xff0c;创建表语句如下 CREATE TABLE girl (id int(11) NOT NULL AUTO_INCREMENT,cup_Size varchar(100) COLLATE utf8mb4_bin DEFAULT NULL,age int(11) DEFAULT NULL,PRIMARY KEY (id) ) ENGINEInnoDB AUTO_INCREMENT4 DEFAULT CHARSETutf8mb4 COLLATEutf8mb4…...

代理IP怎么使用?Mac苹果系统设置http代理IP教程

代理IP是一种通过将请求转发到另一个服务器&#xff0c;以隐藏自己的真实IP地址的服务器。使用代理IP可以保护您的隐私和安全&#xff0c;防止被跟踪或被攻击。在本文中&#xff0c;我们将介绍如何在Mac苹果系统上设置http代理IP教程。 一、了解代理IP 代理IP地址是一种可以用来…...

postgresql_conf中常用配置项

在 PostgreSQL 的 postgresql.conf 配置文件中&#xff0c;有许多常用的配置项&#xff0c;这些配置项可以根据特定需求和性能优化进行调整。以下是一些常用的配置项及其作用&#xff1a; 1. shared_buffers 用于设置 PostgreSQL 实例使用的共享内存缓冲区大小。增加此值可以…...

使用MfgTool烧写前需准备的文件

一. 简介 本文我们就来学习&#xff0c;如何将我们编译的 uboot&#xff0c;zImage&#xff08;内核镜像&#xff09;&#xff0c;xxx.dtb设备树文件&#xff0c;还有制作的根文件系统&#xff0c;这四个文件烧写到开发板中&#xff0c;最后 开发板能正常启动。 本文这里使用…...

SAP UI5 walkthrough step4 XML Views

SAPUI5 指出多种VIEW类型&#xff0c;包括XML,HTML,JavaScript 推荐使用XML&#xff0c;因为可读性更高 我们提前介绍一下MVC架构。 MVC是一种软件架构模式&#xff0c;它包括三个主要组件&#xff1a;模型&#xff08;Model&#xff09;、视图&#xff08;View&#xff09;…...

Java 1对1

文章目录 前言 客户端 服务器端 输出线程端 End 前言 TCP&#xff08;Transmission Control Protocol&#xff09;是一种面向连接的、可靠的网络传输协议&#xff0c;它提供了端到端的数据传输和可靠性保证。 本程序就是基于tcp协议编写而成的。 利用 TCP 协议进行通信的…...