Mybatis如何實現(xiàn)InsertOrUpdate功能
實現(xiàn)InsertOrUpdate功能
需求
最近在項目開發(fā)中遇到這樣一個需求:每天需要對相同的數據(也有可能是不同的)進行兩次入庫操作,數據不存在便insert,存在則update。于是就用到了Mybatis的InsertOrUpdate功能。
實現(xiàn)
每次操作數據庫之前,先根據id查詢有沒有記錄,有則進行update操作,沒有則進行insert操作。
model類代碼如下。其中count為非業(yè)務字段(也不是表sheet中的字段),只是方便Mybatis進行insertOrUpdate操作的附加字段。
import lombok.Data; @Data public class Sheet { /** * 主鍵 */ private String id; /** * 客戶姓名 */ private String customerName; /** * 。。。省略其他字段 */ /** * 該字段為非業(yè)務字段。Mybatis配置文件需要要到該字段,方便進行insertOrUpdate操作 */ private int count; }
Mybatis的mapper.xml配置文件代碼如下。
代碼含義:先執(zhí)行selectKey語句,把結果賦值給Sheet類的count屬性。
- 如果count大于0,表示記錄已存在,則進行update操作。
- 如果count等于0,表示沒有記錄,則進行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標簽可以給update標簽中的parameterType屬性(model類)對應的對象設置屬性值。selectKey標簽的屬性描述:
keyProperty
:selectKey 語句結果應該被設置的目標屬性。此處對應的就是Sheet類的count屬性。resultType
:結果的類型,此處為屬性count的類型。order
:可以被設置為 BEFORE 或 AFTER。BEFORE表示先執(zhí)行selectKey語句,后執(zhí)行update語句;AFTER表示先執(zhí)行update語句,后執(zhí)行selectKey語句。
Mybatis學習筆記:InsertOrUpdate
環(huán)境
- Intellij IDEA : 2021.3
- Mysql:8+
- java:1.8+
前言
以前使用mongodb、JOOQ組件的時候都是有insertOrUpdate的功能,現(xiàn)在使用mybatis似乎沒有提供這種功能。
最近研究了,這個功能其實是mysql提供的,利用的是duplicate key update;
假設,我們有這么一張表:
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 '關聯(lián)id', `type` int(11) NOT NULL DEFAULT '0' COMMENT '0:默認', `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)建時間', `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>
說明:
- on duplicate key update這個是非常關鍵的地方,需要有唯一鍵和主鍵。
- on duplicate key update后面跟著的name=values(name)算是一個固定寫法,作用:動態(tài)的傳入要修改的值。
在MySQL 8.0.20之后,VALUES()在mysql未來的版本會被刪除。
官方建議,使用列別名的方式來寫:
<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
語法: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;
注意:
當使用列別名時,必須在VALUES子句后面使用行別名,即使在后面的子句中不使用行別名。
除了insert into … values 場景,insert into …set場景也適用。
語法和上面是一樣的:
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)在假設我們有這些索引:
唯一索引: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)
主鍵沖突
假設這時,主鍵沖突,那么MySQL就會接著判斷是否 唯一索引沖突:
① 唯一索引不沖突,那么久執(zhí)行更新
② 唯一索引沖突,就會報錯:
1062 - Duplicate entry '0-yutao-yutao111' for key 'template_url.uk_biz_id_code_name', Time: 0.004000s
編輯時,唯一索引的字段不要修改
小結一下:insertOrUpdate的實現(xiàn)是基于mysql的on duplicate key update 來實現(xiàn)的。
使用ON DUPLICATE KEY UPDATE,如果行作為新行插入,則每行受影響的行值為1。如果更新現(xiàn)有行,則每行受影響的行值為2;如果將現(xiàn)有行設置為其當前值,則每行受影響的行值為0(可以通過配置,使其受影響的行值為1)。
官方地址:
13.2.6.2 INSERT … ON DUPLICATE KEY UPDATE Statement
以上為個人經驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
springboot定時任務SchedulingConfigurer異步多線程實現(xiàn)方式
這篇文章主要介紹了springboot定時任務SchedulingConfigurer異步多線程實現(xiàn)方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-04-04Eclipse下基于Java的OpenCV開發(fā)環(huán)境配置教程
這篇文章主要為大家詳細介紹了Eclipse下基于Java的OpenCV開發(fā)環(huán)境配置教程,具有一定的參考價值,感興趣的小伙伴們可以參考一下2019-07-07SpringCloud中的openFeign調用服務并傳參的過程
服務和服務之間通信,不僅僅是調用,往往在調用過程中還伴隨著參數傳遞,接下來重點來看看OpenFeign在調用服務時如何傳遞參數,感興趣的朋友一起看看吧2023-11-11IDEA2022搭建Spring?Cloud多模塊項目的詳細過程
這篇文章主要介紹了IDEA2022搭建Spring?Cloud多模塊項目,網上有很多教程父模塊都是通過maven的方式創(chuàng)建的,然后子模塊是通過Spring?Initalizr方式創(chuàng)建,這種方式父模塊無法管理子模塊的依賴仲裁,需要每個子模塊自行管理,就失去了父模塊的用處了2022-10-10