今天的想法是,要在插入数据库时,如果有某某一个主要字段的值重复,则不插入,否则则插入!
看了一下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@NoArgsConstructorpublic 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=?==> Parameters: 0(Long)<== Total: 0Releasing 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 ( ? )==> Parameters: 1(Integer)<== Updates: 1
不过这个update,不用试我都感觉难搞,因为你如果没有id,那么你传入这个对象的值,可能查出多个对象,那么他要把哪个id映射回来,是吧!
@Testvoid 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,所以啊!赶快看看吧!
UpdateWrapper 小贴士
上面虽然写了updateWrapper可以写一个set属性,有两种情况。
首先,我们一个对象,有5条属性,只有4条有值,1条没有值。
mybatis-plus在执行时,会先去看看你的对象哪条属性有值,哪条没有。
只会更新有值的属性,所以只会更新4个属性,另外一个属性并不会把他置空。
如果你只想改一条属性,也可以多写一个set,不过感觉没啥必要,不过比较灵活的就是。
你想写另外一个值,就可以写进set里。
2021-05-21 13:31:32
我发现一个很垃圾的,前面我吹的那个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就不好用咯!看自己的需求走吧!
INSERT INTO pms_statistic (id,tenantId,tenantName,isDeleted,createTime)VALUES(6257,50,'保存或修改0',1,'2020-01-00') ,(6258,51,'保存或修改1',1,'2020-01-01') ,(6259,52,'保存或修改2',1,'2020-01-02') ,(62510,53,'保存或修改3',1,'2020-01-03') ON DUPLICATE KEY UPDATE tenantId = VALUES(tenantId),tenantName = VALUES(tenantName),isDeleted = VALUES(isDeleted),createTime = VALUES(createTime);
该语句是基于主键(PRIMARY KEY)或唯一索引(UNIQUE INDEX)使用的。
如果已存在该唯一标示或主键就更新(显示受影响行的值:2)
如果不存在该唯一标示或主键则作为新行插入(显示受影响行的值:1)
如上:如果id(6257,6258,6259,62510)存在,根据id更新ON DUPLICATE KEY UPDATE后的字段数据(tenantId = VALUES(tenantId),tenantName = VALUES(tenantName),isDeleted = VALUES(isDeleted),createTime = VALUES(createTime))
执行sql前(存在6257id,其他id不存在)
执行后
6257修改,6258,6259,62510插入
受影响行:5(插入3条,修改1条(修改是两行))
注意:ON DUPLICATE KEY UPDATE只是MySQL的特有语法,并不是SQL标准语法!
来源地址:https://blog.csdn.net/qq_43842093/article/details/131609927