一、前言
在日常業務開發中經常有這樣一個場景,首先創建一條記錄,然后插入到數據庫;如果數據庫已經存在同一主鍵的記錄,則執行update操作,如果不存在,則執行insert操作;
這個操作可以在業務層做,也可以在數據庫層面做;
業務層一般做法是先查詢,如果不存在在插入,如果存在則更新,但是查詢和插入不是原子性操作,在并發量比較高的時候,可能兩個線程都查詢某個記錄不存在,所以會執行兩次插入,然后其中一條必然會因為主鍵(這里說的主鍵不是遞增主鍵)沖突而失敗。
數據庫層mysql中INSERT ... ON DUPLICATE KEY UPDATE就可以做這個事情,并且是原子性操作,本文就來講解的使用。
二、INSERT ... ON DUPLICATE KEY UPDATE命令
2.1單條記錄下使用
INSERT INTO t1 (a,b,c) VALUES (1,2,3)
ON DUPLICATE KEY UPDATE c=c+1;
如上sql假如t1表的主鍵或者UNIQUE 索引是a,那么當執行上面sql時候,如果數據庫里面已經存在a=1的記錄則更新這條記錄的c字段的值為原來值+1,然后返回值為2。如果不存在則插入a=1,b=2,c=3到數據庫,然后返回值為1。
2.2多記錄下使用
INSERT INTO t1 (a,b,c) VALUES (1,2,3),(4,5,6)
ON DUPLICATE KEY UPDATE c=VALUES(c);
三、MyBatis下使用
Mybatis作為經典的數據庫持久層框架,自然要介紹下它下的使用
- 在mapper.xml里面配置如下:
假設a為主鍵
<insert id="insertOrUpdate">
INSERT INTO t1 (a,b,c)
values
<foreach collection="list" item="item" separator=",">
(#{item.a},#{item.b},#{item.c})
</foreach>
ON DUPLICATE KEY UPDATE c=values(c),b=values(b)
</insert>
- 對應的mapper接口可以定義為:
long insertOrUpdate(List<Test> list);
class Test{
private int a;
private int b;
private int c;
...
}
注:mysql中sql字符串大小有限制,我本機的mysql上執行show VARIABLES like '%max_allowed_packet%';結果為max_allowed_packet為4M:
image.png
四、參考
https://dev.mysql.com/doc/refman/5.7/en/insert-on-duplicate.html