Mybatis如何實(shí)現(xiàn)InsertOrUpdate功能
實(shí)現(xiàn)InsertOrUpdate功能
需求
最近在項(xiàng)目開(kāi)發(fā)中遇到這樣一個(gè)需求:每天需要對(duì)相同的數(shù)據(jù)(也有可能是不同的)進(jìn)行兩次入庫(kù)操作,數(shù)據(jù)不存在便insert,存在則update。于是就用到了Mybatis的InsertOrUpdate功能。
實(shí)現(xiàn)
每次操作數(shù)據(jù)庫(kù)之前,先根據(jù)id查詢有沒(méi)有記錄,有則進(jìn)行update操作,沒(méi)有則進(jìn)行insert操作。
model類代碼如下。其中count為非業(yè)務(wù)字段(也不是表sheet中的字段),只是方便Mybatis進(jìn)行insertOrUpdate操作的附加字段。
import lombok.Data;
@Data
public class Sheet {
/**
* 主鍵
*/
private String id;
/**
* 客戶姓名
*/
private String customerName;
/**
* 。。。省略其他字段
*/
/**
* 該字段為非業(yè)務(wù)字段。Mybatis配置文件需要要到該字段,方便進(jìn)行insertOrUpdate操作
*/
private int count;
}Mybatis的mapper.xml配置文件代碼如下。
代碼含義:先執(zhí)行selectKey語(yǔ)句,把結(jié)果賦值給Sheet類的count屬性。
- 如果count大于0,表示記錄已存在,則進(jìn)行update操作。
- 如果count等于0,表示沒(méi)有記錄,則進(jìn)行insert操作。
<update id="insertOrUpdate" parameterType="Sheet" >
<selectKey keyProperty="count" resultType="int" order="BEFORE">
select count(1) from sheet where ID= #{id}
</selectKey>
<if test="count > 0">
update sheet
<set>
<if test="customerName != null and customerName != ''">
CUSTOMER_NAME= #{customerName},
</if>
</set>
where ID = #{id}
</if>
<if test="count==0">
insert into sheet
<trim prefix="(" suffix=")" suffixOverrides=",">
ID,
<if test="customerName != null and customerName != ''">
CUSTOMER_NAME,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
#{id},
<if test="customerName != null and customerName != ''">
#{customerName},
</if>
</trim>
</if>
</update>selectKey標(biāo)簽可以給update標(biāo)簽中的parameterType屬性(model類)對(duì)應(yīng)的對(duì)象設(shè)置屬性值。selectKey標(biāo)簽的屬性描述:
keyProperty:selectKey 語(yǔ)句結(jié)果應(yīng)該被設(shè)置的目標(biāo)屬性。此處對(duì)應(yīng)的就是Sheet類的count屬性。resultType:結(jié)果的類型,此處為屬性count的類型。order:可以被設(shè)置為 BEFORE 或 AFTER。BEFORE表示先執(zhí)行selectKey語(yǔ)句,后執(zhí)行update語(yǔ)句;AFTER表示先執(zhí)行update語(yǔ)句,后執(zhí)行selectKey語(yǔ)句。
Mybatis學(xué)習(xí)筆記:InsertOrUpdate
環(huán)境
- Intellij IDEA : 2021.3
- Mysql:8+
- java:1.8+
前言
以前使用mongodb、JOOQ組件的時(shí)候都是有insertOrUpdate的功能,現(xiàn)在使用mybatis似乎沒(méi)有提供這種功能。
最近研究了,這個(gè)功能其實(shí)是mysql提供的,利用的是duplicate key update;
假設(shè),我們有這么一張表:
CREATE TABLE `relation` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主鍵', `name` varchar(64) NOT NULL DEFAULT '' COMMENT '名稱', `relation_id` varchar(64) NOT NULL DEFAULT '' COMMENT '關(guān)聯(lián)id', `type` int(11) NOT NULL DEFAULT '0' COMMENT '0:默認(rèn)', `is_delete` tinyint(4) NOT NULL DEFAULT '0' COMMENT ' 狀態(tài)值', `create_at` varchar(64) NOT NULL DEFAULT '' COMMENT '創(chuàng)建人', `created` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '創(chuàng)建時(shí)間', `update_at` varchar(64) NOT NULL DEFAULT '' COMMENT '更新人', `updated` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新人', PRIMARY KEY (`id`), UNIQUE KEY `ix_relation_id_type` (`relation_id`,`type`) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
注意: ix_relation_id_type:唯一索引
Dao
@Mapper
public interface FlowModelMapper {
? ? void insertOrUpdate(List<FlowModel> flowModel);
}Mapper XML文件
<insert id="insertOrUpdate">
insert into flow_model(name, relation_id, type, is_delete,create_at,update_at)
values
<foreach collection="list" item="p" index="index" separator=",">
(
#{p.name},
#{p.relationId},
#{p.type},
#{p.isDelete},
#{p.createAt},
#{p.updateAt}
)
</foreach>
on duplicate key update
name=values(name),
update_at=values(update_at)
</insert>
說(shuō)明:
- on duplicate key update這個(gè)是非常關(guān)鍵的地方,需要有唯一鍵和主鍵。
- on duplicate key update后面跟著的name=values(name)算是一個(gè)固定寫法,作用:動(dòng)態(tài)的傳入要修改的值。
在MySQL 8.0.20之后,VALUES()在mysql未來(lái)的版本會(huì)被刪除。
官方建議,使用列別名的方式來(lái)寫:
<insert id="insertOrUpdate">
insert into flow_model(name, relation_id, type, is_delete,create_at,update_at)
values
<foreach collection="list" item="p" index="index" separator=",">
(
#{p.name},
#{p.relationId},
#{p.type},
#{p.isDelete},
#{p.createAt},
#{p.updateAt}
)
</foreach>
AS fm
on duplicate key update
name=fm.name,
update_at=fm.update_at
</insert>
行別名
insert into …values
語(yǔ)法:insert into ...values(...) AS 行別名 ON DUPLICATE KEY UPDATE 使用行別名。
例如:下面的 new就是行別名。
INSERT INTO t1 (a,b,c) VALUES (1,2,3),(4,5,6) AS new ? ON DUPLICATE KEY UPDATE c = new.a+new.b;
列別名
或者是:insert into ...values(...) AS 行別名(列別名,列別名,列別名) ON DUPLICATE KEY UPDATE 使用別名
下面的m,n,p是隨便取的列別名
INSERT INTO t1 (a,b,c) VALUES (1,2,3),(4,5,6) AS new(m,n,p) ? ON DUPLICATE KEY UPDATE c = m+n;
注意:
當(dāng)使用列別名時(shí),必須在VALUES子句后面使用行別名,即使在后面的子句中不使用行別名。
除了insert into … values 場(chǎng)景,insert into …set場(chǎng)景也適用。
語(yǔ)法和上面是一樣的:
INSERT INTO t1 SET a=1,b=2,c=3 AS new ? ON DUPLICATE KEY UPDATE c = new.a+new.b; INSERT INTO t1 SET a=1,b=2,c=3 AS new(m,n,p) ? ON DUPLICATE KEY UPDATE c = m+n;
主鍵和唯一索引
現(xiàn)在假設(shè)我們有這些索引:
唯一索引:biz_id、name、code
主鍵:id
insert into template_url(id,name, code, url, scope, description,
biz_id, create_by, create_user_id, update_by, update_user_id)
values
(
1,'yutao','yutao','www.baidu.com','yutao','yutao',0,'yutao',0,'yutao',0
)
ON DUPLICATE KEY UPDATE
name=values(name),
description=values(description),
url=values(url),
scope=values(scope),
update_by=values(update_by),
update_user_id=values(update_user_id)
主鍵沖突
假設(shè)這時(shí),主鍵沖突,那么MySQL就會(huì)接著判斷是否 唯一索引沖突:
① 唯一索引不沖突,那么久執(zhí)行更新
② 唯一索引沖突,就會(huì)報(bào)錯(cuò):
1062 - Duplicate entry '0-yutao-yutao111' for key 'template_url.uk_biz_id_code_name', Time: 0.004000s
編輯時(shí),唯一索引的字段不要修改
小結(jié)一下:insertOrUpdate的實(shí)現(xiàn)是基于mysql的on duplicate key update 來(lái)實(shí)現(xiàn)的。
使用ON DUPLICATE KEY UPDATE,如果行作為新行插入,則每行受影響的行值為1。如果更新現(xiàn)有行,則每行受影響的行值為2;如果將現(xiàn)有行設(shè)置為其當(dāng)前值,則每行受影響的行值為0(可以通過(guò)配置,使其受影響的行值為1)。
官方地址:
13.2.6.2 INSERT … ON DUPLICATE KEY UPDATE Statement
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
java本服務(wù)如何調(diào)用本服務(wù)接口
這篇文章主要介紹了java本服務(wù)如何調(diào)用本服務(wù)接口問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2025-04-04
java利用delayedQueue實(shí)現(xiàn)本地的延遲隊(duì)列
這篇文章主要給大家介紹了java利用delayedQueue實(shí)現(xiàn)本地的延遲隊(duì)列的相關(guān)資料,文中介紹的非常詳細(xì),相信對(duì)大家具有一定的參考價(jià)值,需要的朋友們下面來(lái)一起看看吧。2017-04-04
詳解Java的MyBatis框架中的緩存與緩存的使用改進(jìn)
很多人在使用MyBatis的緩存后經(jīng)常會(huì)遇到MySQL分頁(yè)查詢的顯示問(wèn)題,針對(duì)于此,這里我們就來(lái)詳解Java的MyBatis框架中的緩存與緩存的使用改進(jìn),首先來(lái)回顧一下MyBatis的緩存機(jī)制與執(zhí)行:2016-06-06
Java編程線程同步工具Exchanger的使用實(shí)例解析
這篇文章主要介紹了Java編程線程同步工具Exchanger的使用實(shí)例解析,分享了相關(guān)代碼示例,小編覺(jué)得還是挺不錯(cuò)的,具有一定借鑒價(jià)值,需要的朋友可以參考下2018-02-02
Mybatis配置之typeAlias標(biāo)簽的用法
這篇文章主要介紹了Mybatis配置之typeAlias標(biāo)簽的用法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-07-07
如何通過(guò)Java代碼實(shí)現(xiàn)KMP算法
這篇文章主要介紹了如何通過(guò)Java代碼實(shí)現(xiàn)KMP算法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-11-11

