深圳企业网站制作公司哪家好/黄页推广平台有哪些
代码展示
1.数据实体类
@Entity
public class User {@PrimaryKey(autoGenerate = true)private long id;private String name;private String age;private String sex;public User(String name, String age, String sex) {this.name = name;this.age = age;this.sex = sex;}public long getId() {return id;}public void setId(long id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getAge() {return age;}public void setAge(String age) {this.age = age;}public String getSex() {return sex;}public void setSex(String sex) {this.sex = sex;}
}
说明:用户表,包括id(作为主键,自动递增)、name、age、sex四个字段
2.Dao类
@Dao
public interface UserDao {@Updatevoid updateUser(User user);}
说明:只展示更新语句
3.用户仓库类
/*** 用户仓库类,负责用户数据的增删改查操作。*/
public class UserRepository {private final UserDao userDao;/*** 构造函数,初始化用户数据访问对象。** @param database 应用数据库实例。*/public UserRepository(AppDatabase database) {this.userDao = database.getUserDao();}/*** 更新用户信息。如果用户不存在,则操作失败。** @param user 要更新的用户对象。* @return 返回操作结果,成功或失败。*/@Transactionpublic UserRepositoryResult updateUser(User user) {// 确认用户存在if (userDao.findUserByName(user.getName()) == null) {return new UserRepositoryResult(UserRepositoryResult.Type.FAILURE,RESULT_MESSAGE_USER_NOT_EXIST);}userDao.updateUser(user);return UserRepositoryResult.success();}}
说明:只展示update实现
4.MainViewModel
/*** 主要的ViewModel类,用于处理与用户相关的数据操作。*/
public class MainViewModel extends ViewModel {// 用户数据仓库接口private final UserRepository userRepository;// 执行器服务,用于在后台线程中执行数据库操作private static final ExecutorService EXECUTOR_SERVICE = Executors.newSingleThreadExecutor();/*** 构造函数,初始化用户数据仓库。** @param userRepository 用户数据仓库实例。*/public MainViewModel(UserRepository userRepository) {this.userRepository = userRepository;}// 用于存储更新用户操作结果的LiveData对象private final MutableLiveData<UserRepositoryResult> updateUserResult = new MutableLiveData<>();/*** 获取更新用户操作的结果。** @return UserRepositoryResult 更新操作的结果。*/public LiveData<UserRepositoryResult> getUpdateUserResult() {return updateUserResult;}// 更新用户public void updateUser(final User user) {EXECUTOR_SERVICE.execute(() -> {updateUserResult.postValue(userRepository.updateUser(user));});}/*** ViewModel工厂类,用于创建MainViewModel实例。*/public static class Factory extends ViewModelProvider.NewInstanceFactory {// 用户数据仓库实例private final UserRepository userRepository;/*** 构造函数,初始化用户数据仓库。** @param userRepository 用户数据仓库实例。*/public Factory(UserRepository userRepository) {this.userRepository = userRepository;}/*** 创建MainViewModel实例。** @param modelClass ViewModel的类类型。* @return MainViewModel 实例。*/@NonNull@Overridepublic <T extends ViewModel> T create(@NonNull Class<T> modelClass) {return (T) new MainViewModel(userRepository);}}}
5.MainActivity
/*** 主活动类,负责管理应用程序的主要界面。*/
public class MainActivity extends AppCompatActivity {private MainViewModel viewModel; // 视图模型,用于管理活动背后的业务逻辑private ActivityMainBinding binding; // 数据绑定实例,用于简化UI更新private UserRepository userRepository;/*** 在活动创建时调用。** @param savedInstanceState 如果活动之前被销毁,这参数包含之前的状态。如果活动没被销毁之前,这参数是null。*/@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);// 启用边缘到边缘的UIEdgeToEdge.enable(this);// 设置数据绑定binding = DataBindingUtil.setContentView(this, R.layout.activity_main);// 设置视图的内边距,以适应系统栏位的高度ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);return insets;});userRepository = new UserRepository(AppDatabase.getDatabase(MVVMApplication.getInstance()));// 初始化视图模型viewModel = new ViewModelProvider(this, new MainViewModel.Factory(userRepository)).get(MainViewModel.class);binding.setViewModel(viewModel);initListeners();initObserver();}/*** 初始化视图监听器。*/private void initListeners() {// 清空输入框操作监听binding.btnClearEdit.setOnClickListener(v -> {clearEditText();});// 更新用户操作监听binding.btnUpdate.setOnClickListener(v -> {Log.i("swy","update按钮被点击");User userChecked = checkUserInfo();if (userChecked != null) {Log.i("swy","更新用户");viewModel.updateUser(userChecked);}});}private void initObserver() {// 观察更新结果viewModel.getUpdateUserResult().observe(this, result -> {Log.i("swy","updateresult发生变化");if (result.getType() == UserRepositoryResult.Type.SUCCESS) {showToast("更新成功");} else {showToast(result.getErrorMessage());}});}// 封装对用户信息输入的验证private User checkUserInfo() {String name = binding.etName.getText().toString();if (name.isEmpty()) {showToast("请输入姓名");return null;}String age = binding.etAge.getText().toString();if (age.isEmpty()) {showToast("请输入年龄");return null;}String sex = binding.etSex.getText().toString();if (sex.isEmpty()) {showToast("请输入性别");return null;}return new User(name, age, sex);}// 封装对姓名输入的检查private String checkName() {String name = binding.etName.getText().toString();if (name.isEmpty()) {showToast("请输入姓名");return "";}return name;}// 清除编辑文本框中的内容private void clearEditText() {binding.etName.setText("");binding.etAge.setText("");binding.etSex.setText("");}// 简化Toast消息的显示private void showToast(String message) {Toast.makeText(this, message, Toast.LENGTH_SHORT).show();}}
至此,一套MVVM架构的Room数据库Update的语法即调用完成,感兴趣的同学可以多看一下这个流程和代码,观察一下是否有问题,多观察几分钟,分析一下流程,因为或许你正在跟我一样经历这个问题,或者未来可能被这类问题给绕进去。
好的,直接来说结果,上面的代码看似是完整的,且调用无误的。
但是里面有一个比较大的问题,这个问题在根儿上,也就是数据实体类,我们再次查看数据实体
@PrimaryKey(autoGenerate = true)private long id;private String name;private String age;private String sex;
前面也说了,这里面id为自增主键。
学过数据库的都知道,主键是用来区分两条数据的,比如说我这里有两条数据
id = 1,name = 张三,age = 18,sex = 男
和
id = 2,name = 张三,age = 18,sex = 男
假如有这么两条数据,id为主键的话,虽然名字年龄性别都是一样的张三,但是对于数据库而言,这是两条数据,对于用户表而言,这是两个同名的人
比如说,我给用户展现的是这样一个页面,支持常规的增删改查,但是用户只可以输入name、age、sex这三个数据
如果第一次用户输入了“name = 张三,age = 18,sex = 男”,点击增加按钮,显然是可以成功的
那么当用户再次输入“name = 张三,age = 18,sex = 男”,这里就有两种做法
第一种:允许输入
上面说了,user表以id为主键,所以从insert语句的执行来看,没有问题,就是完完全全的两条数据,这也是比较符合现实的,因为中国这么大,人这么多,肯定有很频繁的重名现象,所以一般我们来区分人并不是以姓名来区分,而是采用身份证号(ID),而后再因为身份证号码只有一个且比较重要不能随便示人,同时又要保证关联到人,我们现在一般用手机号来区分人,当然,手机号都有实名认证,即关联了身份证,且一个身份证可以办理多个手机号。当然,那个问题与本文无关,只是说到这里了。
第二种:不允许输入
这种设计方案一般在游戏里面比较常见,比如说,游戏刚开服,大家一窝蜂冲进去很重要一件事是干什么,抢注游戏昵称,因为如果某一个昵称比较好听或者寓意比较好,大家都想用,但是游戏的系统只允许一个人用,也就是不允许相同昵称出现这种策略。当然现在的大部分新游戏都是支持相同昵称的,所以在那些游戏的个人页面都支持一键赋值uid,这个uid就是游戏里面的身份证了,然后我们通过uid进行好友查找。
好了,上面描述了两种管理用户的策略,只是作为辅助思考
回归正题,讨论我前面展示的代码中的错误问题
通过上面的分析,我们知道了把id作为主键之后,其他属性并不足以区分用户这件关键信息了
所以来看这两部分的代码
1.从用户输入,组装更新User对象
// 封装对用户信息输入的验证private User checkUserInfo() {String name = binding.etName.getText().toString();if (name.isEmpty()) {showToast("请输入姓名");return null;}String age = binding.etAge.getText().toString();if (age.isEmpty()) {showToast("请输入年龄");return null;}String sex = binding.etSex.getText().toString();if (sex.isEmpty()) {showToast("请输入性别");return null;}return new User(name, age, sex);}// 更新用户操作监听binding.btnUpdate.setOnClickListener(v -> {Log.i("swy","update按钮被点击");User userChecked = checkUserInfo();if (userChecked != null) {Log.i("swy","更新用户");viewModel.updateUser(userChecked);}});
2.传入user对象,执行Update操作
@Transactionpublic UserRepositoryResult updateUser(User user) {// 确认用户存在if (userDao.findUserByName(user.getName()) == null) {return new UserRepositoryResult(UserRepositoryResult.Type.FAILURE,RESULT_MESSAGE_USER_NOT_EXIST);}userDao.updateUser(user);return UserRepositoryResult.success();}@Updatevoid updateUser(User user);
其实,说到这里,我相信大家已经看出来哪里出现问题了
我就不妨再说的细致一些吧,这是Room框架对于@Update注解的处理逻辑:
当您使用@Update注解进行用户数据更新时,Room库会根据传入的User对象的主键(即id)来定位到要更新的记录。具体处理逻辑如下:
a. 查找匹配的主键:Room会根据User对象的id值在数据库中查找具有相同id的记录。
b. 执行更新:如果找到了匹配的记录,则按照传入的User对象中的非空属性值更新对应的字段。这意味着如果您仅修改了name、age或sex中的某个属性值,并将这个更新后的User对象传递给@Update方法,只会更新这些已更改的属性,而不会影响其他未修改的属性或主键id。
c. 无匹配记录则不执行任何操作:如果数据库中找不到与传入User对象id相匹配的记录,Room将不做任何更新操作。
所以,问题就出现在了我们调用updateUser方法时,传入的user对象本身,即
new User(name, age, sex);
可见,这里面是没有id的,那么没有id意味着什么,意味着id为null
对于Room而言,id为null时,它会怎么去理解我们传入的user对象呢——不做任何更新操作
所以,问题就找到了
也非常好解决,有两种解决方法,第一种就是给用户提供id的输入框(这种可以联想一下上面说过的游戏里面输入uid查找好友)
我们这里就简单一点吧
看一下我们的UserRepository中的具体调用
// 确认用户存在if (userDao.findUserByName(user.getName()) == null) {return new UserRepositoryResult(UserRepositoryResult.Type.FAILURE,RESULT_MESSAGE_USER_NOT_EXIST);}
是的,你更新用户,首先得先确定用户存在对吧。
我是因为考虑一切从简,所以设计时是不允许相同name出现的,所以直接用name来查重了,当然也可以通过id来查找User
直接展示最终的处理办法
@Transactionpublic UserRepositoryResult updateUser(User user) {// 确认用户存在User findUser = userDao.findUserByName(user.getName());if (findUser == null) {return new UserRepositoryResult(UserRepositoryResult.Type.FAILURE,RESULT_MESSAGE_USER_NOT_EXIST);}user.setId(findUser.getId());userDao.updateUser(user);return UserRepositoryResult.success();}
通过name找到数据库中的旧的User对象,因为id是主键,所以id不会更改,直接把旧的id塞到我们的新的user对象中,然后再传给updateUser方法,即可以实现Update方法了
相关文章:

Android Room 记录一个Update语句不生效的问题解决记录
代码展示 1.数据实体类 Entity public class User {PrimaryKey(autoGenerate true)private long id;private String name;private String age;private String sex;public User(String name, String age, String sex) {this.name name;this.age age;this.sex sex;}public …...

使用SpringBoot3+Vue3开发公寓管理系统
项目介绍 公寓管理系统可以帮助公寓管理员更方便的进行管理房屋。功能包括系统管理、房间管理、租户管理、收租管理、房间家具管理、家具管理、维修管理、维修师傅管理、退房管理。 功能介绍 系统管理 用户管理 对系统管理员进行管理,新增管理员,修改…...

有且仅有的10个常见的排序算法,东西不多,怎么就背不下来呢
就这么跟你说吧,面试中肯定会出排序算法的算法题,你只需要背下来代码背下来他们的时间复杂度和空间复杂度就能蒙混过关。 不管你是前端还是后端,关于排序的算法有且仅有这 10个,如果你用心了,怎么会记不住呢。看完这篇…...

Mac安装配置ElasticSearch和Kibana 8.13.2
系统环境:Mac M1 (MacOS Sonoma 14.3.1) 一、准备 从Elasticsearch:官方分布式搜索和分析引擎 | Elastic上下载ElasticSearch和Kibana 笔者下载的是 elasticsearch-8.13.2-darwin-aarch64.tar.gz kibana-8.13.2-darwin-aarch64.tar.gz 并放置到个人…...

javaWeb项目-快捷酒店管理系统功能介绍
项目关键技术 开发工具:IDEA 、Eclipse 编程语言: Java 数据库: MySQL5.7 框架:ssm、Springboot 前端:Vue、ElementUI 关键技术:springboot、SSM、vue、MYSQL、MAVEN 数据库工具:Navicat、SQLyog 1、Spring Boot框架 …...

闲不住,手写一个数据库文档生成工具
shigen坚持更新文章的博客写手,擅长Java、python、vue、shell等编程语言和各种应用程序、脚本的开发。记录成长,分享认知,留住感动。 个人IP:shigen 逛博客的时候,发现了一个很有意思的文章:数据库表结构导…...

在群晖上安装GPT4Free
什么是 GPT4Free ? GPT4Free 简称 G4F,是一个强大的大型语言模型命令行界面(LLM-CLI),旨在去中心化并提供免费访问先进人工智能技术的能力。G4F 的目标是通过提供用户友好和高效的工具,使人工智能民主化&am…...

C# 语言类型(四)—传递参数及其修饰符
总目录 C# 语法总目录 参考链接: C#语法系列:C# 语言类型(一)—预定义类型值之数值类型 C#语法系列:C# 语言类型(二)—预定义类型之字符串及字符类型简述 C#语法系列:C# 语言类型(三)—数组/枚举类型/结构体 C#语法系列:C# 语言类型(四)—传递参数及其修饰符 C#语法…...

刷穿力扣006-剑指offer一数组——02寻找目标值-二维数组
刷穿力扣006-剑指offer<一>数组——02寻找目标值-二维数组 基本面试题都是我带大家刷的力扣热题100和剑指offer的75道题,建议刷两遍!(ps:想找工作实习的同学,文末有面试八股和简历模板) 题目: 语言…...

爬虫(小案例)
点开其中一个链接, http://desk.zol.com.cn/dongman/huoyingrenzhe/(前面为浏览器自动补全,在代码里需要自己补全) 可以看到图片的下载地址以及打开本图集下一张图片的链接 了解完网站的图片构造后动手写代码,我们筛…...

环信 IM 客户端将适配鸿蒙 HarmonyOS
自华为推出了自主研发操作系统鸿蒙 HarmonyOS 后,国内许多应用软件开始陆续全面兼容和接入鸿蒙操作系统。环信 IM 客户端计划将全面适配统鸿蒙 HarmonyOS ,助力开发者快速实现社交娱乐、语聊房、在线教育、智能硬件、社交电商、在线金融、线上医疗等广泛…...

伪元素的使用
.box::after{content: ;display: block;// 定义元素位置margin-top: 12rpx;margin-right: 20rpx;// 定义元素宽高width: 36rpx;height: 36rpx;// background-image无法引用本地资源,故需要用网络地址background-image: url($urlcalendar.png);background-size: 100%…...

TensorFlow学习之:高级应用和扩展
生成对抗网络:了解GAN的基本原理,使用TensorFlow实现简单的GAN 生成对抗网络(Generative Adversarial Networks,GAN)由两部分组成:生成器(Generator)和判别器(Discrimin…...

maya模板导入动画
maya模板导入动画,第一帧为模板姿态 要将一个FBX文件中的动画数据导入另一个FBX文件的模板,并使得第一帧是模板的初始姿势,第二帧开始是动画,你可以在Maya中采用以下步骤来操作: 步骤 1: 导入模板FBX 首先ÿ…...

【微信小程序之分包】
微信小程序之分包 什么是分包分包的好处分包前的结构图分包后的结构图分包的加载规则分包的体积限制使用分包打包原则引用原则独立分包独立分包的配置方法独立分包的引用原则分包预下载配置分包的预下载分包预下载限制 什么是分包 分包指的是把一个完整小程序项目,…...

STM32-ADC(独立模式、双重模式)
ADC简介 18个通道:外部信号源就是16个GPIO回。在引脚上直接接模拟信号就行了,不需要侄何额外的电路。引脚就直接能测电压。2个内部信号源是内部温度传感器和内部参考电压。 逐次逼近型ADC: 它是一个独立的8位逐次逼近型ADC芯片,这个ADC0809是…...

03.卸载MySQL
卸载MySQL 1.Windows卸载MySQL8 停止服务 用命令停止或者在服务中停止都可以 net stop mysql(服务名字可以去服务里面看一下)控制面板卸载MySQL 卸载MySQL8.0的程序可以和其他桌面应用程序一样直接在控制面板选择卸载程序,并在程序列表中…...

2024.4.13 蓝桥杯软件类C++B组山东省赛 小记
大三老狗了 , 还是把精力放在考研上了 ,所以只是蓝桥杯的前一晚上把常用算法翻了翻。 其实还做了一场小模拟,两个题分值200分我狂砍了17分,bfs写半小时写不明白,所以晚上已经是心如死灰了,所以就早早睡觉了…...

Windows下IntelliJ IDEA远程连接服务器中Hadoop运行WordCount(详细版)
使用IDEA直接运行Hadoop项目,有两种方式,分别是本地式:本地安装HadoopIDEA;远程式:远程部署Hadoop,本地安装IDEA并连接, 本文介绍第二种。 一、安装配置Hadoop (1)虚拟机伪分布式 见上才艺&a…...

【每日刷题】Day16
【每日刷题】Day16 🥕个人主页:开敲🍉 🔥所属专栏:每日刷题🍍 🌼文章目录🌼 1. 24. 两两交换链表中的节点 - 力扣(LeetCode) 2. 160. 相交链表 - 力扣&…...

【K8s】:在 Kubernetes 集群中部署 MySQL8.0 高可用集群(1主2从)
【K8s】:在 Kubernetes 集群中部署 MySQL8.0 高可用集群(1主2从) 一、准备工作二、搭建nfs服务器2.1 安装 NFS 服务器软件包(所有节点执行)2.2 设置共享目录2.3 启动 NFS 服务器2.4 设置防火墙规则(可选&am…...

Vue内置组件TransitionGroup详细介绍
<TransitionGroup> 是一个内置组件,用于对 v-for 列表中的元素或组件的插入、移除和顺序改变添加动画效果。 和 <Transition> 的区别 <TransitionGroup> 支持和 <Transition> 基本相同的 props、CSS 过渡 class 和 JavaScript 钩子监听器…...

【机器学习300问】71、神经网络中前向传播和反向传播是什么?
我之前写了一篇有关计算图如何帮助人们理解反向传播的文章,那为什么我还要写这篇文章呢?是因为我又学习了一个新的方法来可视化前向传播和反向传播,我想把两种方法总结在一起,方便我自己后续的复习。对了顺便附上往期文章的链接方…...

【ZZULIOJ】1067: 有问题的里程表(Java)
目录 题目描述 输入 输出 样例输入 Copy 样例输出 Copy 提示 code 题目描述 某辆汽车有一个里程表,该里程表可以显示一个整数,为该车走过的公里数。然而这个里程表有个毛病:它总是从3变到5,而跳过数字4,里程表…...

A21 STM32_HAL库函数 之 I2c通用驱动程序 -- B -- 所有函数的介绍及使用
A21 STM32_HAL库函数 之 I2c通用驱动程序 -- B -- 所有函数的介绍及使用 1 该驱动函数预览1.12 HAL_I2C_Master_Sequential_Receive_IT1.13 HAL_I2C_Slave_Transmit_IT1.14 HAL_I2C_Slave_Receive_IT1.15 HAL_I2C_Slave_Sequential_Transmit_IT1.16 HAL_I2C_Slave_Sequential_R…...

简介:Asp.Net Core进阶高级编程教程
课程简介目录 🚀前言一、课程背景二、课程目的三、课程特点四、课程适合人员六、最后 🚀前言 本文是《.Net Core进阶编程课程》教程专栏的导航站(点击链接,跳转到专栏主页,欢迎订阅,持续更新…)…...

Linux系统中LVM与磁盘配额
目录 一、LVM逻辑卷管理 二、LVM的管理命令 物理卷管理 卷组管理 逻辑卷管理 *创建并使用LVM步骤 三、磁盘配额概述 实现磁盘限额的条件 Linux 磁盘限额的特点 四、磁盘配额管理 磁盘限额 一、LVM逻辑卷管理 能够在保持现有数据不变的情况下动态调整磁盘容量&#…...

手机重启手app没了
发现公司有些Android球机设备,安装了一些app,重启后app没了,还有公司的一些Android手机,原来是没问题的,不知道哪天起,只要重启,新安装的软件就会没了,很神奇。后来发现,…...

github上传代码
偷一下懒,把链接贴一下,后续再补充。 1.下载Git 【学习笔记】上传代码到GitHub(保姆级教程) 2.如何创建GitHub仓库 手把手教你在github上传文件 3.如何删掉GitHub仓库 github如何删除仓库或项目? 4.遇到的错误 …...

Qt+vstudio2022的报错信息积累
从今天开始记录一下平常开发工作中的报错记录,后续有错误动态补充! 报错信息:【MSB8041】此项目需要 MFC 库。从 Visual Studio 安装程序(单个组件选项卡)为正在使用的任何工具集和体系结构安装它们。 解决: 背景:换…...