Redian新闻
>
MybatisPlus 使用 saveOrUpdate() 方法踩坑记录(慎用)

MybatisPlus 使用 saveOrUpdate() 方法踩坑记录(慎用)

公众号新闻

点击上方“芋道源码”,选择“设为星标

管她前浪,还是后浪?

能浪的浪,才是好浪!

每天 10:33 更新文章,每天掉亿点点头发...

源码精品专栏

 
来源:blog.csdn.net/weixin_45369440/
article/details/116044771

今天的想法是,要在插入数据库时,如果有某某一个主要字段的值重复,则不插入,否则则插入!看了一下mybatis-Plus是有这个saveOrUpdate 方法!

原本使用save时是没有问题了,改成saveOrUpdate 用了一下就报错了。

com.baomidou.mybatisplus.core.exceptions.MybatisPlusException: error: can not execute. because can not find column for id from entity!

就是这个mybatisPlus不能找到哪个是主键字段,因为这个saveOrUpdate默认是根据主键执行操作的!

所有需要在原本的实体类的主键头上,打个@TableId,如下,后面是对应数据库的字段,已经主键自动递增。

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Subject {

  @TableId(value = "subject_Code", type = IdType.AUTO)
  private long subjectCode;

  private String subjectNameCn;

  private String subjectNameEn;

  private String subjectHref;

  private long subjectParentCode;

  private long levelCode;

  private int isDelete;

  private long operateTimestamp;


}

不过还有个问题,就是这个是根据主键做操作的,但是我主键本来就是自动递增肯定不会有问题的,接下来就是想个办法,让他根据指定字段做操作,好像是有提供了一个口子。

// 根据updateWrapper尝试更新,否继续执行saveOrUpdate(T)方法
boolean saveOrUpdate(T entity, Wrapper<T> updateWrapper);

我再去看一下怎么操作的!

研究尝试了半天,终于搞出来了,可能是很少有人会像我这样做吧!所以我自己尝试了下。

saveOrUpdate不使用条件构造器时,会先做根据主键查询,如果查出来的结果为0,那么就执行插入操作,如果查出来的结果不为0,则执行更新操作。

但是一般情况下,主键都不会重复啊!所有我就用条件构造器Wrapper!

UpdateWrapper<Subject> subject_name_cn = new UpdateWrapper<Subject>()
   .eq("subject_Name_Cn", subjectNameCn);
subjectService.saveOrUpdate(subject,subject_name_cn );

这样改变后的结果就是会先执行修改,如果执行一条,则执行成功,如果执行结果为0,再执行根据主键查询,然后做插入操作!

其实有点多此一举的感觉,因为既然都已经更新不到结果了,那么肯定是没有这个字段咯!

不过转念一想,你是指定字段没有,又不是主键没有!

但是主键自增那肯定没有啊!

所有我又想到一个骚操作,我不传UpdateWrapper而传QueryWrapper会怎么样呢!

会不会加在查询条件种呢!我丢进去没有报错,有点小激动,不知道结果如何!

QueryWrapper<Subject> subject_name_cn1 = new QueryWrapper<Subject>()
                    .eq("subject_Name_Cn", subjectNameCn);
subjectService.saveOrUpdate(subject,subject_name_cn1);

好吧!上来全给我Update了!丝毫不留情面!我把数据删了再试试!

好吧!然并卵!幻想破灭!跟传UpdateWrapper没有区别!~告辞!

看了一下源码,默认参数是Wrapper类型,然后根据条件构造器更新,成功则返回,不成功则走无条件构造器的方法。

default boolean saveOrUpdate(T entity, Wrapper<T> updateWrapper) {
    return this.update(entity, updateWrapper) || this.saveOrUpdate(entity);
}

我感觉应该加个类型判断!

 if(updateWrapper instanceof QueryWrapper){
  //去拼接查询语句!
 }
  if(updateWrapper instanceof UpdateWrapper){
  //去拼接更新语句!
 }

这样就不会只根据ID来死查了!

为什么要用updateWrapper?

它与queryWrapper的区别就是:updateWrapper用set来设置修改的数据;queryWrapper应用select来设置要查出来的数据。

saveOrUpdate 是否有映射id

我们知道mybatis在插入时,会映射id,但是如果是saveOrUpdate会怎么样呢?

比如我saveOrUpdate()后,需要用他的id,但是我传进去的对象是没有id的。

 @Test
 void saveOrUpdate(){
       UserText userText = new UserText();
       userText.setUserSex(Sex.MAN);
       boolean b = userTextService.saveOrUpdate(userText);
       System.out.println(userText.getUserId());
   }

可以看到他先通过id查了没有再进行插入,然后返回新的id。

==>  Preparing: SELECT user_id,user_name,user_sex,start_time FROM user_text WHERE user_id=?
==> Parameters0(Long)
<==      Total: 0
Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@6d0fe80c]
Fetched SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@6d0fe80c] from current transaction
==>  Preparing: INSERT INTO user_text ( user_sex ) VALUES ( ? )
==> Parameters1(Integer)
<==    Updates: 1

不过这个update,不用试我都感觉难搞,因为你如果没有id,那么你传入这个对象的值,可能查出多个对象,那么他要把哪个id映射回来,是吧!

@Test
void saveOrUpdate(){
     UserText userText = new UserText();
     userText.setUserSex(Sex.MAN);
     UpdateWrapper<UserText> objectUpdateWrapper = new UpdateWrapper<UserText>()
             .eq("user_sex",Sex.MAN);
     boolean b = userTextService.saveOrUpdate(userText,objectUpdateWrapper);
     System.out.println(userText.getUserId());
 }

但还是试一下,当我们加了一个UpdateWrapper后,有执行成功,执行了3条,返回了id为0。

但是这次加了wrapper,我再试试如果只插入一条,会怎么样。哈哈,不去读源码去debug,就只能这样试试了,莫怪。

诶,对啊,我去看看源码先,看能不能看出什么门道。

之前好像也有看了点源码。两种不同构造的方法,执行的逻辑也不一样。

boolean saveOrUpdate(T entity)

default boolean saveOrUpdate(T entity, Wrapper<T> updateWrapper) {
 return this.update(entity, updateWrapper) || this.saveOrUpdate(entity);
}

区别不大,就是会多执行一步更新,如果执行成功就直接走,执行不成功再根据这个对象做saveOrUpdate

进去翻了翻就是,如果通过id查到值,就根据id更新,不然就做新增。

所以也就不用试了,还是自己手写一个吧,如果需要返回id的话。

慎用!

细思极恐,当你是主键自动生成的数据,一定要写UpdateWrapper,不然你必然是一直插入!完全不会更新,因为默认是用id查询的。

而主键生成的数据,一般都不会去写一个id,所以啊!赶快看看吧!

基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

  • 项目地址:https://gitee.com/zhijiantianya/ruoyi-vue-pro
  • 视频教程:https://doc.iocoder.cn/video/

UpdateWrapper 小贴士

上面虽然写了updateWrapper可以写一个set属性,有两种情况。

首先,我们一个对象,有5条属性,只有4条有值,1条没有值。

mybatis-plus在执行时,会先去看看你的对象哪条属性有值,哪条没有。

只会更新有值的属性,所以只会更新4个属性,另外一个属性并不会把他置空。

基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

  • 项目地址:https://gitee.com/zhijiantianya/yudao-cloud
  • 视频教程:https://doc.iocoder.cn/video/

后续

我发现一个很垃圾的,前面我吹的那个updateWrapper的set多牛逼,其实是我想的太美了,他只是在原本的基础上再加一个字段!我吐了!

UpdateWrapper<GameScorePo> updateWrapper = new UpdateWrapper<GameScorePo>()
                   .eq("game_id",gameScorePo.getGameId())
                   .eq("team_id",gameScorePo.getTeamId())
                   .eq("quarter",gameScorePo.getQuarter())
                   .set("score",gameScorePo.getScore());

           gameScoreService.saveOrUpdate(gameScorePo,updateWrapper);

这样的执行结果是这样的!

两个score,我吐了!

难道是我打开的姿势不对?

查了一下知道这个set怎么样了

就是不要丢对象,丢一个空的对象,这样就能set了!

单独的set好用,但是用在saveOrUpdate就不好用咯!看自己的需求走吧!



欢迎加入我的知识星球,一起探讨架构,交流源码。加入方式,长按下方二维码噢

已在知识星球更新源码解析如下:

最近更新《芋道 SpringBoot 2.X 入门》系列,已经 101 余篇,覆盖了 MyBatis、Redis、MongoDB、ES、分库分表、读写分离、SpringMVC、Webflux、权限、WebSocket、Dubbo、RabbitMQ、RocketMQ、Kafka、性能测试等等内容。

提供近 3W 行代码的 SpringBoot 示例,以及超 4W 行代码的电商微服务项目。

获取方式:点“在看”,关注公众号并回复 666 领取,更多内容陆续奉上。

文章有帮助的话,在看,转发吧。

谢谢支持哟 (*^__^*)

微信扫码关注该文公众号作者

戳这里提交新闻线索和高质量文章给我们。
相关阅读
专访“MySQL 之父”:我曾创造 MySQL,也将颠覆 MySQLBarclays JetBlue Plus 信用卡【80k 开卡奖励】工农兵大学巴黎凡尔赛游记 (五)有效去除杂念的方法(实用)毛泽东时代工业化时的鞍钢宪法Zhihu Accused of Using Watermarks in Screenshots to Locate Users28 People Charged Over Brutal Attack on Women in Tangshan结束了,就这样疫情结束了bāo huā shēng?bō huā shēng?MyBatis引起的线程池线程打满问题排查过程Mybatis 框架下 SQL 注入审计分析A Top Archaeology Institute Struggles to Hire Archaeologists萌宠丨哈雷最近的update,我们战胜癌症两年半啦赴华:2022年9月20日-24日,美国出发入境韩国后回国记录(韩国签证、入韩准备、在韩经历等)源码学习之MyBatis的底层查询原理Elon Musk 声称 Apple 威胁要把 Twitter 从 Apple Store 下架娱乐消费更贵了,苹果将在加拿大提高 Apple Music 和 Apple TV Plus 的价格荣耀Play6C今日开售,骁龙480Plus 5G,1099元起2023年3月8号Diversity峰会预热 | 亚洲创业投资期刊Diversity & Inclusion论坛门票免费拿[电脑] 开箱一个显示器,记录十年来最大的一次update一个人的徒步,900公里法国之路+世界尽头:D41~圣路上的明星看了我的 mybatis-plus 用法,全公司同事开始悄悄模仿了。。apt 的 update 和 upgrade 命令的区别是什么? | Linux 中国即将抵达柏(bǎi)林站 | 2022武汉4号线柏林电影周推荐展映片单公布面试官:select......for update 会锁表还是锁行?我拴 Q 了!!线上踩坑记:项目中一次OOM的分析定位排查过程!2022年12月27日美国驻华使团领事服务更新|Update on Consular ServicesGigabyte GeForce RTX 2070 Super Aorus 8G Graphics Card专访FunPlus业务发展主管:State of Survival如何成为SLG全球大赢家?美股SPAC|专注于亚太地区的 SPAC Plutonian Acquisition 申请 5000 万美元 IPO侦探电影《Knives Out》续集《Glass Onion: A Knives Out Mystery》烂番茄评价出炉22-23财年塔州技术移民最新Update!橙色通道获邀的难度变大!Corsair HX1000 1000W 80 Plus Platinum Modular Power Supply
logo
联系我们隐私协议©2024 redian.news
Redian新闻
Redian.news刊载任何文章,不代表同意其说法或描述,仅为提供更多信息,也不构成任何建议。文章信息的合法性及真实性由其作者负责,与Redian.news及其运营公司无关。欢迎投稿,如发现稿件侵权,或作者不愿在本网发表文章,请版权拥有者通知本网处理。