當前位置: 妍妍網 > 碼農

MyBatisPlus 使用 saveOrUpdate() 的坑

2023-05-21碼農

大家好,我是磊哥。

天的想法是,要在插入資料庫時,如果有某某一個主要欄位的值重復,則不插入,否則則插入!

看了一下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 classSubject{
@TableId(value = "subject_Code", type = IdType.AUTO)
privatelong subjectCode;
private String subjectNameCn;
private String subjectNameEn;
private String subjectHref;
privatelong subjectParentCode;
privatelong levelCode;
privateint isDelete;
privatelong operateTimestamp;
}


不過還有個問題,就是這個是根據主鍵做操作的,但是我主鍵本來就是自動遞增肯定不會有問題的,接下來就是想個辦法,讓他根據指定欄位做操作,好像是有提供了一個口子。

// 根據updateWrapper嘗試更新,否繼續執行saveOrUpdate(T)方法
booleansaveOrUpdate(T entity, Wrapper<T> updateWrapper);

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

研究嘗試了半天,終於搞出來了,可能是很少有人會像我這樣做吧!所以我自己嘗試了下。

如果你近期準備面試跳槽,建議在ddkk.com線上刷題,涵蓋 一萬+ 道 Java 面試題,幾乎覆蓋了所有主流技術面試題,還有市面上最全的技術五百套,精品系列教程,免費提供。

當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型別,然後根據條件構造器更新,

成功則返回,

不成功則走無條件構造器的方法。

defaultbooleansaveOrUpdate(T entity, Wrapper<T> updateWrapper){
returnthis.update(entity, updateWrapper) || this.saveOrUpdate(entity);
}

我感覺應該加個型別判斷!

if(updateWrapper instanceof QueryWrapper){
去拼接查詢語句!
 }
if(updateWrapper instanceof UpdateWrapper){
去拼接更新語句!
 }

這樣就不會只根據ID來死查了!

2023-05-21 11:07:24

我才終於明白~

為什麽要用updateWrapper了!

它與queryWrapper的區別就是。

updateWrapper用set來設定修改的數據。

queryWrapper套用select來設定要查出來的數據。

哈哈,這個還是很重要的!

saveOrUpdate 是否有對映id

我們知道mybatis在插入時,會對映id,但是如果是saveOrUpdate會怎麽樣呢?

比如我saveOrUpdate()後,需要用他的id,但是我傳進去的物件是沒有id的。

@Test
voidsaveOrUpdate(){
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: 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( ? )
==> Parameters: 1(Integer)
<== Updates: 1

不過這個update,不用試我都感覺難搞,因為你如果沒有id,那麽你傳入這個物件的值,可能查出多個物件,那麽他要把哪個id對映回來,是吧!

如果你近期準備面 試跳槽,建議在ddkk.com線上刷題,涵蓋 一萬+ 道 Java 面試題,幾乎覆蓋了所有主流技術面試題,還有市面上最全的技術五百套,精品系列教程,免費提供。

@Test
voidsaveOrUpdate(){
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,就只能這樣試試了,莫怪。

誒,對啊,我去看看源碼先,看能不能看出什麽門道。

之前好像也有看了點源碼。兩種不同構造的方法,執行的邏輯也不一樣。

booleansaveOrUpdate(T entity)
defaultbooleansaveOrUpdate(T entity, Wrapper<T> updateWrapper){
returnthis.update(entity, updateWrapper) || this.saveOrUpdate(entity);
}

區別不大,就是會多執行一步更新,如果執行成功就直接走,執行不成功再根據這個物件做saveOrUpdate。

進去翻了翻就是,如果透過id查到值,就根據id更新,不然就做新增。

所以也就不用試了,還是自己手寫一個吧,如果需要返回id的話。

慎用!

細思極恐,當你是主鍵自動生成的數據,一定要寫UpdateWrapper,不然你必然是一直插入!完全不會更新,因為預設是用id查詢的。

而主鍵生成的數據,一般都不會去寫一個id,所以啊!趕快看看吧!

UpdateWrapper 小貼士

上面雖然寫了updateWrapper可以寫一個set內容,有兩種情況。

首先,我們一個物件,有5條內容,只有4條有值,1條沒有值。

mybatis-plus在執行時,會先去看看你的物件哪條內容有值,哪條沒有。

只會更新有值的內容,所以只會更新4個內容,另外一個內容並不會把他置空。

如果你只想改一條內容,也可以多寫一個set,不過感覺沒啥必要,不過比較靈活的就是。

如果你近期準備面試跳槽,建議在ddkk.com線上刷題,涵蓋 一萬+ 道 Java 面試題,幾乎覆蓋了所有主流技術面試題,還有市面上最全的技術五百套,精品系列教程,免費提供。 你想寫另外一個值,就可以寫進set裏。

2023-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就不好用咯!看自己的需求走吧!

🔥 磊哥私藏精品 熱門推薦 🔥