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

【鸿蒙HarmonyOS NEXT】数据存储之分布式键值数据库

【鸿蒙HarmonyOS NEXT】数据存储之分布式键值数据库

  • 一、环境说明
  • 二、分布式键值数据库介绍
  • 三、示例代码加以说明
  • 四、小结

一、环境说明

  1. DevEco Studio 版本:
    在这里插入图片描述

  2. API版本:以12为主
    在这里插入图片描述

二、分布式键值数据库介绍

KVStore简介

分布式键值数据库为应用程序提供不同设备间数据库的分布式协同能力。通过调用分布式键值数据库各个接口,应用程序可将数据保存到分布式键值数据库中,并可对分布式键值数据库中的数据进行增加、删除、修改、查询、同步等操作。该模块提供以下分布式键值数据库相关的常用功能:

  • KVManager:分布式键值数据库管理实例,用于获取数据库的相关信息。
  • KVStoreResultSet:提供获取数据库结果集的相关方法,包括查询和移动数据读取位置等。
  • Query:使用谓词表示数据库查询,提供创建Query实例、查询数据库中的数据和添加谓词的方法。
  • SingleKVStore:单版本分布式键值数据库,不对数据所属设备进行区分,提供查询数据和同步数据的方法。
  • DeviceKVStore:设备协同数据库,继承自SingleKVStore,以设备维度对数据进行区分,提供查询数据和同步数据的方法。

应用场景:

键值型数据库存储键值对形式的数据,当需要存储的数据没有复杂的关系模型,比如存储商品名称及对应价格、员工工号及今日是否已出勤等,由于数据复杂度低,更容易兼容不同数据库版本和设备类型,因此推荐使用键值型数据库持久化此类数据。

常用接口说明:

键值型数据库持久化功能的相关接口,大部分为异步接口。异步接口均有callback和Promise两种返回形式,下表均以callback形式为例,其他接口请查看官网:分布式键值数据库接口说明在这里插入图片描述

三、示例代码加以说明

沿用【鸿蒙HarmonyOS NEXT】页面之间相互传递参数博文中的代码,进行测试。
使用场景说明用户信息需要在整个app中全局获取到,需要将用户信息在登录的时候存储到分布式键值数据库中。

代码改写如下:

  1. LoginPage完整代码如下:
    import { router } from '@kit.ArkUI';// 引入Context相关
    import { common } from '@kit.AbilityKit';
    import { hilog } from '@kit.PerformanceAnalysisKit';// 引入preferences相关
    import { distributedKVStore } from '@kit.ArkData';
    import { util } from '@kit.ArkTS';
    import { BusinessError } from '@kit.BasicServicesKit';// 定义ihlog日志常量相关
    const TAG: string = '[LoginPage_Context]';
    const DOMAIN_NUMBER: number = 0xFF00;@Preview
    @Entry
    @Component
    struct LoginPage {@State message: string = '登录页';@State btnMsg: string = '登录';@State account: string = ''; // 账号状态变量@State password: string = ''; // 密码状态变量@State isShowProgress: boolean = false; // 显示进度指示器的状态变量// 获取Contextprivate context = getContext(this) as common.UIAbilityContext;build() {Column() {Text(this.message).id('HelloWorld').fontSize(20).fontWeight(FontWeight.Bold).width('100%').height(50).textAlign(TextAlign.Center).backgroundColor(0xF1F3F5)Image($r('app.media.startIcon')).width(150).height(150).margin({ top: 40, bottom: 40 })TextInput({ placeholder: '请输入手机号' }).maxLength(11)// 最大长度.type(InputType.Number)// 输入类型为数字.inputStyle()// 应用自定义样式.onChange((value: string) => {this.account = value; // 更新账号状态})Line().lineStyle() // 应用自定义Line样式// 密码输入框TextInput({ placeholder: '请输入密码' }).maxLength(12)// 最大长度.type(InputType.Password)// 输入类型为密码.inputStyle()// 应用自定义样式.onChange((value: string) => {// TODO: 生产环境需要使用正则表达式对手机号进行验证this.password = value; // 更新密码状态})Line().lineStyle() // 应用自定义Line样式Button(this.btnMsg).width('80%').margin({ top: 100 }).height(50).onClick(() => {if (this.account === undefined || this.account === '') {console.info('请输入账号')return}if (this.password === undefined || this.password === '') {console.info('请输入密码')return}// 使用ArkData分布式键值数据库 以Key/Value形式存储用户信息数据let kvManager: distributedKVStore.KVManager | undefined = undefined;const kvManagerConfig: distributedKVStore.KVManagerConfig = {context: this.context,bundleName: 'com.suben.hellotest'};try {// 创建KVManager实例kvManager = distributedKVStore.createKVManager(kvManagerConfig);console.info('Succeeded in creating KVManager.');} catch (e) {let error = e as BusinessError;console.error(`Failed to create KVManager. Code:${error.code},message:${error.message}`);}// 继续创建获取数据库if (kvManager !== undefined) {kvManager = kvManager as distributedKVStore.KVManager;//进行后续操作let kvStore: distributedKVStore.SingleKVStore | undefined = undefined;try {const options: distributedKVStore.Options = {createIfMissing: true,encrypt: false,backup: false,autoSync: false,// kvStoreType不填时,默认创建多设备协同数据库kvStoreType: distributedKVStore.KVStoreType.SINGLE_VERSION,// 多设备协同数据库:kvStoreType: distributedKVStore.KVStoreType.DEVICE_COLLABORATION,securityLevel: distributedKVStore.SecurityLevel.S1};kvManager.getKVStore<distributedKVStore.SingleKVStore>('storeId', options,(err, store: distributedKVStore.SingleKVStore) => {if (err) {console.error(`Failed to get KVStore: Code:${err.code},message:${err.message}`);return;}console.info('Succeeded in getting KVStore.');kvStore = store; // 请确保获取到键值数据库实例后,再进行相关数据操作if (kvStore !== undefined) {kvStore = kvStore as distributedKVStore.SingleKVStore;//进行后续操作,如将用户信息插入数据库中进行保存const KEY_ACCOUNT = 'account';const VALUE_ACCOUNT = this.account;const KEY_PASSWORD = 'password';const VALUE_PASSWORD = this.password;try {kvStore.put(KEY_ACCOUNT, VALUE_ACCOUNT, (err) => {if (err !== undefined) {console.error(`Failed to put account data. Code:${err.code},message:${err.message}`);return;}console.info('Succeeded in putting account data.');});kvStore.put(KEY_PASSWORD, VALUE_PASSWORD, (err) => {if (err !== undefined) {console.error(`Failed to put password data. Code:${err.code},message:${err.message}`);return;}console.info('Succeeded in putting password data.');});} catch (e) {let error = e as BusinessError;console.error(`An unexpected error occurred. Code:${error.code},message:${error.message}`);}}});} catch (e) {let error = e as BusinessError;console.error(`An unexpected error occurred. Code:${error.code},message:${error.message}`);}}// 跳转到首页router.pushUrl({url: 'pages/HomePage',params: {account: this.account,password: this.password}})})}.height('100%').width('100%').padding(0)}
    }// TextInput组件的自定义样式扩展
    @Extend(TextInput)
    function inputStyle() {.placeholderColor(Color.Gray) // 占位符颜色.height(50) // 输入框高度.fontSize(15) // 字体大小.backgroundColor(0xF1F3F5) // 背景颜色.width('90%') // 宽度为父组件的100%.padding({ left: 12 }) // 左侧填充.margin({ top: 15 }) // 上方边距
    }// Line组件的自定义样式扩展
    @Extend(Line)
    function lineStyle() {.width('100%') // 宽度为父组件的100%.height(1) // 高度.backgroundColor(0xF1F3F5) // 背景颜色
    }
    
  2. HomePage完整代码如下:
    import { router } from '@kit.ArkUI';
    import { distributedKVStore } from '@kit.ArkData';
    import { common } from '@kit.AbilityKit'
    import { util } from '@kit.ArkTS';
    import { hilog } from '@kit.PerformanceAnalysisKit';
    import { BusinessError } from '@kit.BasicServicesKit';// 定义ihlog日志常量相关
    const TAG: string = '[HomePage_Context]';
    const DOMAIN_NUMBER: number = 0xFF01;@Preview
    @Entry
    @Component
    struct HomePage {@State message: string = '首页';// 获取前一个页面传递过来的数据@State account: string = ''@State password: string = ''// 获取Contextprivate context = getContext(this) as common.UIAbilityContext;aboutToAppear(): void {// 使用ArkData KV数据库 获取用户数据// this.account = this.dataPreferences.getSync('account', 'default').toString(); // 'default' 可以换成其他的,如null,但这个参数必须给// hilog.info(DOMAIN_NUMBER, TAG, `account: ${this.account}`);// // 当获取的值为带有特殊字符的字符串时,需要将获取到的Uint8Array转换为字符串// let uInt8Array2 : preferences.ValueType = this.dataPreferences.getSync('password', new Uint8Array(0));// let textDecoder = util.TextDecoder.create('utf-8');// this.password = textDecoder.decodeToString(uInt8Array2 as Uint8Array);// hilog.info(DOMAIN_NUMBER, TAG, `password: ${this.password}`);let kvManager: distributedKVStore.KVManager | undefined = undefined;const kvManagerConfig: distributedKVStore.KVManagerConfig = {context: this.context,bundleName: 'com.suben.hellotest'};try {// 创建KVManager实例kvManager = distributedKVStore.createKVManager(kvManagerConfig);console.info('Succeeded in creating KVManager.');} catch (e) {let error = e as BusinessError;console.error(`Failed to create KVManager. Code:${error.code},message:${error.message}`);}// 继续创建获取数据库if (kvManager !== undefined) {kvManager = kvManager as distributedKVStore.KVManager;//进行后续操作let kvStore: distributedKVStore.SingleKVStore | undefined = undefined;try {const options: distributedKVStore.Options = {createIfMissing: true,encrypt: false,backup: false,autoSync: false,// kvStoreType不填时,默认创建多设备协同数据库kvStoreType: distributedKVStore.KVStoreType.SINGLE_VERSION,// 多设备协同数据库:kvStoreType: distributedKVStore.KVStoreType.DEVICE_COLLABORATION,securityLevel: distributedKVStore.SecurityLevel.S1};kvManager.getKVStore<distributedKVStore.SingleKVStore>('storeId', options,(err, store: distributedKVStore.SingleKVStore) => {if (err) {console.error(`Failed to get KVStore: Code:${err.code},message:${err.message}`);return;}console.info('Succeeded in getting KVStore.');kvStore = store; // 请确保获取到键值数据库实例后,再进行相关数据操作if (kvStore !== undefined) {kvStore = kvStore as distributedKVStore.SingleKVStore;const KEY_ACCOUNT = 'account';const KEY_PASSWORD = 'password';// 获取账户kvStore.get(KEY_ACCOUNT, (err, data) => {if (err != undefined) {console.error(`Failed to get ACCOUNT data. Code:${err.code},message:${err.message}`);return;}console.info(`Succeeded in getting ACCOUNT data. Data:${data}`);this.account = data.toString()});//获取密码kvStore.get(KEY_PASSWORD, (err, data) => {if (err != undefined) {console.error(`Failed to get PASSWORD data. Code:${err.code},message:${err.message}`);return;}console.info(`Succeeded in getting PASSWORD data. Data:${data}`);this.password = data.toString()});}});} catch (e) {let error = e as BusinessError;console.error(`An unexpected error occurred. Code:${error.code},message:${error.message}`);}}}build() {Column() {Text(this.message).fontSize(30).width('100%').height(50).textAlign(TextAlign.Center).backgroundColor(0xF1F3F5)Blank().height(120)Text(`接收到的用户名:${this.account}`).fontSize(20).width('100%').height(50).padding({ left: 12, right: 12 })Text(`接收到的密码:${this.password}`).fontSize(20).width('100%').height(50).padding({ left: 12, right: 12 })Button('返回上一页').width('80%').margin({ top: 120 }).height(50).onClick(() => {// 返回登录页面router.showAlertBeforeBackPage({ message: '确认返回上一页吗?' })router.back({url: 'pages/LoginPage',params: {msg: 'homepage'}})})}.height('100%').width('100%')}
    }

测试步骤如下:
3. 打开模拟器,并将代码部署到模拟器上,当模拟器正常运行代码后,输入用户名和密码
在这里插入图片描述

  1. 点击手机模拟器上应用的登录按钮,跳转到了首页,然后再从首页返回登录页,查看控制台日志,内容如截图红色框或者首页文字所示:
    在这里插入图片描述

四、小结

通过上述的说明和示例演示,相信大家已经很清楚分布式键值数据库读写数据的用法了。细心的读者朋友可能会问,如何删除数据呢?或者有何约束和限制呢?感兴趣的读者朋友可以对照鸿蒙官网上的示例尝试下,看看使用键值型数据库持久化此类数据是否可以正常运行呢?欢迎大家的留言,我们在留言区进行讨论。

相关文章:

【鸿蒙HarmonyOS NEXT】数据存储之分布式键值数据库

【鸿蒙HarmonyOS NEXT】数据存储之分布式键值数据库 一、环境说明二、分布式键值数据库介绍三、示例代码加以说明四、小结 一、环境说明 DevEco Studio 版本&#xff1a; API版本&#xff1a;以12为主 二、分布式键值数据库介绍 KVStore简介&#xff1a; 分布式键值数据库…...

基于springboot+小程序的儿童预防接种预约管理系统(疫苗1)(源码+sql脚本+视频导入教程+文档)

&#x1f449;文末查看项目功能视频演示获取源码sql脚本视频导入教程视频 1、项目介绍 本儿童预防接种预约微信小程序可以实现管理员和用户。 1、管理员功能有个人中心&#xff0c;用户管理&#xff0c;儿童信息管理&#xff0c;疫苗信息管理&#xff0c;儿童接种管理&#x…...

计算物理精解【8】-计算原理精解【5】

文章目录 logistic模型多元回归分析多元回归分析概览1. 多元回归的概念与重要性2. 多元回归在实际应用中的例子3. 多元回归在预测和解释数据中的优势和局限性4. 多元回归的优缺点及改进建议 多元线性回归分析详解一、原理二、性质三、计算四、例子与例题五、应用场景六、优缺点…...

【Linux】 tcp | 解除服务器对tcp连接的限制 | 物联网项目配置

一、修改tcp连接限制 1、编辑 vi /etc/sysctl.conf 2、内容 net.ipv4.tcp_keepalive_intvl 75 net.ipv4.tcp_keepalive_probes 9 net.ipv4.tcp_keepalive_time 7200 net.ipv4.ip_local_port_range 1024 65535 net.ipv4.ip_conntrack_max 20000 net.ipv4.tcp_max_tw_bucket…...

如何隐藏Windows10「安全删除硬件」里的USB无线网卡

本方法参照了原文《如何隐藏Windows10「安全删除硬件」里的USB无线网卡》里面的方法&#xff0c;但是文章中的描述我的实际情况不太一样&#xff0c;于是我针对自己的实际情况进行了调整&#xff0c;经过测试可以成功隐藏Windows10「安全删除硬件」里的USB无线网卡。 先说一下…...

【QT Quick】基础语法:导入外部JS文件及调试

在 QML 中&#xff0c;可以使用 JavaScript 来实现业务逻辑的灵活性和简化开发。接下来我们会学习如何导入 JavaScript 文件&#xff0c;并在 QML 中使用它&#xff0c;同时也会介绍如何调试这些 JavaScript 代码。 导入 JavaScript 文件 在 QML 中导入 JavaScript 文件的方式…...

【质优价廉】GAP9 AI算力处理器赋能智能可听耳机,超低功耗畅享未来音频体验!

当今世界&#xff0c;智能可听设备已经成为了流行趋势。随后耳机市场的不断成长起来&#xff0c;消费者又对AI-ANC&#xff0c;AI-ENC&#xff08;环境噪音消除&#xff09;降噪的需求逐年增加&#xff0c;但是&#xff0c;用户对于产品体验的需求也从简单的需求&#xff0c;升…...

用Flutter几年了,Flutter每个版本有什么区别?

用Flutter几年了&#xff0c;你知道Flutter每个版本有什么区别吗&#xff1f;不管是学习还是面试我们可能都需要了解这个信息。 Flutter 每个版本的用法基本都是一样的&#xff0c;每隔几天或者几周就会更新一个版本&#xff0c; 2018 年 12 月 5 日发布了1.x 版本&#…...

解决Qt每次修改代码后首次运行崩溃,后几次不崩溃问题

在使用unique_ptr声明成员变量后&#xff0c;我习惯性地在初始化构造列表中进行如下构造&#xff1a; 注意看&#xff0c;我将m_menuBtnGroup的父类指定为ui->center_menu_widget&#xff0c;这便是导致崩溃的根本原因&#xff0c;解决办法便是先用this初始化&#xff0c;后…...

语言的变量交换

不用第三个变量交换两个变量在面试题或者笔试题中无数次被提到&#xff0c;事实上&#xff0c;有些答案是理论性的&#xff0c;不是准确的。以整型为例&#xff0c;如下对比不同交换方式的差异。 不同的交换方式 利用中间变量c a; 00C02533 8B 45 F8 mov eax,dword ptr [a] 0…...

【muduo源码分析】「阻塞」「非阻塞」「同步」「异步」

欢迎来到 破晓的历程的 博客 ⛺️不负时光&#xff0c;不负己✈️ 文章目录 引言何为「muduo库」安装muduo库阻塞、非阻塞、同步、异步数据准备数据准备 引言 从本篇博客开始&#xff0c;我会陆续发表muduo库源码分析的相关文章。感谢大家的持续关注&#xff01;&#xff01;…...

顶顶通呼叫中心中间件-机器人话术挂机后是否处理完成事件

前言 问题&#xff1a;机器人放音的过程中&#xff0c;如果用户直接挂机就会继续匹配下一个流程&#xff0c;如果匹配上的是放音节点&#xff0c;还会进行放音&#xff0c;那么在数据库表中就会多出一条放音记录。 解决方法 一、话术添加一个全局挂机节点 需要在话术中添加一…...

Springboot Mybatis 动态SQL

动态SQL <?xml version"1.0" encoding"UTF-8" ?> <!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""https://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace"com.wzb.SqlImprove2024…...

ORM的了解

什么是ORM&#xff1f;为什么要用ORM&#xff1f;-CSDN博客 C高级编程&#xff08;99&#xff09;面向资源的设计思想&#xff08;ORM&#xff09;_c orm-CSDN博客 ORM:Object-Relational-Mapping 对象关系映射 -------------------------- 我想对数据库中的表A进行增删改…...

关于大模型的10个思考

9月28日&#xff0c;第四届“青年科学家50论坛”在南方科技大学举行&#xff0c;美国国家工程院外籍院士沈向洋做了《通用人工智能时代&#xff0c;我们应该怎样思考大模型》的主题演讲&#xff0c;并给出了他对大模型的10个思考。 以下是他10个思考的具体内容&#xff1a; 1…...

CFR( Java 反编译器)---> lambda 表达式底层实现机制

一、安装教程 CFR&#xff08;Class File Reader&#xff09;是一个流行的Java反编译器&#xff0c;它可以将编译后的.class文件或整个.jar文件转换回Java源代码。以下是CFR的下载和使用教程&#xff1a; 下载CFR 访问CFR的官方网站或GitHub仓库&#xff1a;CFR的最新版本和所…...

《C++多态性:开启实际项目高效编程之门》

在 C的广阔编程世界中&#xff0c;多态性是一个强大而富有魅力的特性。它为程序员提供了极大的灵活性和可扩展性&#xff0c;使得代码能够更加优雅地应对复杂的业务需求。在实际项目中&#xff0c;理解和正确应用 C的多态性至关重要&#xff0c;它可以显著提高代码的质量、可维…...

UDS_5_输入输出控制功能单元

目录 一. 0x2F服务 一. 0x2F服务 InputOutputControlByIdentifier(0x2F)服务 用于替换服务器输入信号的值或内部功能控制电子系统的某个输出(执行器) •请求报文 A_Data Byte Parameter Name Cvt Byte Value #1 InputOutputControlByIdentifier Request SID M 0x2F dataI…...

CAD快捷键

CAD快捷键 功能快捷键描述直线L点PO多段线PL多用于描边构造线XL无限长直线射线RAY样条曲线SPL绘制光滑曲线–––圆弧A圆C矩形REC正多边形POL–––填充H圆角F倒角CHA–––打断BR分解X合并J–––创建块B插入块I 功能快捷键描述移动M复制CO擦除E修剪TR延伸EX拉伸S镜像MI偏移…...

Spring6梳理12——依赖注入之注入Map集合类型属性

以上笔记来源&#xff1a; 尚硅谷Spring零基础入门到进阶&#xff0c;一套搞定spring6全套视频教程&#xff08;源码级讲解&#xff09;https://www.bilibili.com/video/BV1kR4y1b7Qc 12 依赖注入之注入Map集合类型属性 12.1 创建Student类和Teacher类 Student类中创建了run…...

LeetCode - 394. 字符串解码

题目 394. 字符串解码 - 力扣&#xff08;LeetCode&#xff09; 思路 使用两个栈&#xff1a;一个存储重复次数&#xff0c;一个存储字符串 遍历输入字符串&#xff1a; 数字处理&#xff1a;遇到数字时&#xff0c;累积计算重复次数左括号处理&#xff1a;保存当前状态&a…...

【磁盘】每天掌握一个Linux命令 - iostat

目录 【磁盘】每天掌握一个Linux命令 - iostat工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景 注意事项 【磁盘】每天掌握一个Linux命令 - iostat 工具概述 iostat&#xff08;I/O Statistics&#xff09;是Linux系统下用于监视系统输入输出设备和CPU使…...

python爬虫:Newspaper3k 的详细使用(好用的新闻网站文章抓取和解析的Python库)

更多内容请见: 爬虫和逆向教程-专栏介绍和目录 文章目录 一、Newspaper3k 概述1.1 Newspaper3k 介绍1.2 主要功能1.3 典型应用场景1.4 安装二、基本用法2.2 提取单篇文章的内容2.2 处理多篇文档三、高级选项3.1 自定义配置3.2 分析文章情感四、实战案例4.1 构建新闻摘要聚合器…...

Ascend NPU上适配Step-Audio模型

1 概述 1.1 简述 Step-Audio 是业界首个集语音理解与生成控制一体化的产品级开源实时语音对话系统&#xff0c;支持多语言对话&#xff08;如 中文&#xff0c;英文&#xff0c;日语&#xff09;&#xff0c;语音情感&#xff08;如 开心&#xff0c;悲伤&#xff09;&#x…...

汇编常见指令

汇编常见指令 一、数据传送指令 指令功能示例说明MOV数据传送MOV EAX, 10将立即数 10 送入 EAXMOV [EBX], EAX将 EAX 值存入 EBX 指向的内存LEA加载有效地址LEA EAX, [EBX4]将 EBX4 的地址存入 EAX&#xff08;不访问内存&#xff09;XCHG交换数据XCHG EAX, EBX交换 EAX 和 EB…...

聊一聊接口测试的意义有哪些?

目录 一、隔离性 & 早期测试 二、保障系统集成质量 三、验证业务逻辑的核心层 四、提升测试效率与覆盖度 五、系统稳定性的守护者 六、驱动团队协作与契约管理 七、性能与扩展性的前置评估 八、持续交付的核心支撑 接口测试的意义可以从四个维度展开&#xff0c;首…...

Android Bitmap治理全解析:从加载优化到泄漏防控的全生命周期管理

引言 Bitmap&#xff08;位图&#xff09;是Android应用内存占用的“头号杀手”。一张1080P&#xff08;1920x1080&#xff09;的图片以ARGB_8888格式加载时&#xff0c;内存占用高达8MB&#xff08;192010804字节&#xff09;。据统计&#xff0c;超过60%的应用OOM崩溃与Bitm…...

使用Spring AI和MCP协议构建图片搜索服务

目录 使用Spring AI和MCP协议构建图片搜索服务 引言 技术栈概览 项目架构设计 架构图 服务端开发 1. 创建Spring Boot项目 2. 实现图片搜索工具 3. 配置传输模式 Stdio模式&#xff08;本地调用&#xff09; SSE模式&#xff08;远程调用&#xff09; 4. 注册工具提…...

动态 Web 开发技术入门篇

一、HTTP 协议核心 1.1 HTTP 基础 协议全称 &#xff1a;HyperText Transfer Protocol&#xff08;超文本传输协议&#xff09; 默认端口 &#xff1a;HTTP 使用 80 端口&#xff0c;HTTPS 使用 443 端口。 请求方法 &#xff1a; GET &#xff1a;用于获取资源&#xff0c;…...

深入理解Optional:处理空指针异常

1. 使用Optional处理可能为空的集合 在Java开发中&#xff0c;集合判空是一个常见但容易出错的场景。传统方式虽然可行&#xff0c;但存在一些潜在问题&#xff1a; // 传统判空方式 if (!CollectionUtils.isEmpty(userInfoList)) {for (UserInfo userInfo : userInfoList) {…...