背景:在一个实体中,有自增主键id,且有唯一键username的时候,要求当username存在时进行更新,不存在时就进行插入

问题:

对大数据量进行批量存储,项目中使用的时候jpa,jpa中有个**saveAll()**的方法可以很好的解决这个“当数据存在时进行更新,不存在时进行插入”问题,但是有这么一个问题,就是当保存的这个实体中有主键且有唯一键的时候这个时候,保存的时候当username重复的时候就会报错,提示Duplicate entry * for key *

解决方法

DDL语句
create table if not exists user
(
	id bigint auto_increment
		primary key,
	age bigint null,
	email varchar(255) null,
	username varchar(255) not null,
	constraint UK_kmv82yj5iq260s9i3rjlfdy5r
		unique (username)
);
/*
*用insert into table (...) values (...) ON DUPLICATE KEY UPDATE column = VALUES(column) *,column2 = VALUES(column2) 语法,这种写法后续会被废弃,有另一种新的写法
* insert into user(...) values (...) as new ON DUPLICATE KEY UPDATE column =new.column  ,column2 = new.column2;
*/
insert into user(age,username,email) values (23,'张三','wewe@qq.com') ON DUPLICATE KEY UPDATE age = VALUES(age) ,email = VALUES(email);
insert into user(age,username,email) values (25,'老七','qwqw@qq.com') as new ON DUPLICATE KEY UPDATE age =new.age ,email = new.email;

如上代码,该table需要有主键或唯一建,解释一下以上代码,一开始先是插入,当字段username在数据库中已经有“张三”这条数据时,会更新update后面的字段,以上面代码为例就是更新age和email这两个字段,其他字段不做更新

此处有点奇怪,就是明明只更新了一条数据,但是影响的行数是2

java 存在则插入不存在则新增 jpa存在更新,不存在就新建_mysql


看了官方的说法,解释如下

With ON DUPLICATE KEY UPDATE, the affected-rows value per row is 1 if the row is inserted as a new row, 2 if an existing row is updated, and 0 if an existing row is set to its current values
就是说如果影响一条则为插入,如果影响条数为2则为更新,如果为0则没有变化

注意,当你执行了更新操作之后,自增的id会多加一,也就是说,更新的后最大的id为12的话,再插入一条记录的时候,id会为14

此处有另外一种写法

/*
*replace into table(...) values(...)
*/
replace into user(age,username,email) values (23,'张三','wewe@qq.com')

以上代码也会“更新”数据,但是replace的原理是,当唯一键重复时,删除改行数据然后再插入该条数据,像上面的user表,如果我执行了replace之后,张三这条数据的自增id会“变为”最新的,原有的数据被删除调了,新增的数据为新插入的数据,这时候张三的id就会为最新的id,如果不想看文字的话或者看不懂的话,看下图

执行replace前的数据如下:

java 存在则插入不存在则新增 jpa存在更新,不存在就新建_数据_02


执行replace into user(age,username,email) values (23,‘张三’,‘wewe@qq.com’)之后可以看到,影响了两条记录,而且张三的id变成4了

java 存在则插入不存在则新增 jpa存在更新,不存在就新建_java 存在则插入不存在则新增_03


java 存在则插入不存在则新增 jpa存在更新,不存在就新建_java_04


注意这里有个坑:当你的主键id同时作为外键去关联了其他表的时候,删除不掉id(或者说会导致你关联的其他表的数据变为脏数据),一般这时候执行replace会报错,所以这个replace方法慎用