首先,ON DUPLICATE KEY UPDATE很好用,只要建唯一键索引就可以和使用insertOrUpdateBatch方法一样快乐,并且还可以自定义更新的字段。
可是,真的这么好?
答案是不是,在多线程的情况下疯狂进行插入的时候,就会报错。
可能说不就是报错嘛,我进行数据补偿一下就是
但是!!!这个东西会导致死锁,锁表的,淦,不要问我为什么知道。。。。。
# mysql:Unknown error 1205/锁表 解决方法
出了问题,懂了吧
那需要再插入的时候进行非id唯一字段条件更新,怎么办?
先说我:
如果简单,真的让id就是此唯一字段,这种很快乐,用insertOrUpdateBatch就行
我现在处理是,代码里面先查询,再分出新增集合和修改集合,再分别进行批量新增和批量修改的方式进行
方案嘛,选自己喜欢的,不管怎么都要按自己情况取舍
方案:
1、你要不id就是此唯一字段,这种很快乐,用insertOrUpdateBatch就行
2、case when 这种方式实现的批量更新操作效率很低,而且,当更新的字段很多时,SQL语句会特别长。
<update id="updateByIds">
update tb_user
<trim prefix="set" suffixOverrides=",">
<trim prefix="name = case" suffix="end,">
<foreach collection="list" item="i" index="index">
when id= #{i.id,jdbcType=VARCHAR} then #{i.name,jdbcType=VARCHAR}
</foreach>
</trim>
<trim prefix="weight = case" suffix="end,">
<foreach collection="list" item="i" index="index">
when id= #{i.id,jdbcType=VARCHAR} then #{i.weight,jdbcType=DECIMAL}
</foreach>
</trim>
<trim prefix="high = case" suffix="end,">
<foreach collection="list" item="i" index="index">
when id= #{i.id,jdbcType=VARCHAR} then #{i.high,jdbcType=DECIMAL}
</foreach>
</trim>
</trim>
where id in
<foreach collection="list" item="item" open="(" close=")" separator=",">
#{item.id,jdbcType=VARCHAR}
</foreach>
</update>
3、replace into 这个和ON DUPLICATE KEY UPDATE一样有问题的
<insert id="updateBatch" parameterType="java.util.List">
replace into t_output_calendar (
index, cal_date, user_type, create_time, modify_time, delete_flag
) values
<foreach collection="list" item="item" index="index" separator=",">
(
#{item.index,jdbcType=INTEGER},
#{item.calDate,jdbcType=TIMESTAMP},
#{item.type,jdbcType=TINYINT},
#{item.createTime,jdbcType=TIMESTAMP},
#{item.modifyTime,jdbcType=TIMESTAMP},
#{item.deleteFlag,jdbcType=TINYINT}
)
</foreach>
</insert>
4、写触发器,成本高、还慢
5、foreach成多条sql,一般般(注意:Mybatis映射文件中的sql语句默认是不支持以" ; " 结尾的,也就是不支持多条sql语句的执行。所以需要在连接mysql的url上加 &allowMultiQueries=true 这个才可以执行。)
<update id="updateBatch" parameterType="java.util.List">
<foreach collection="list" item="item" index="index" open="" close="" separator=";">
update tableName
<set>
name=${item.name},
name2=${item.name2}
</set>
where id = ${item.id}
</foreach>
</update>
6、代码里面先查询,再分出新增集合和修改集合,再分别进行批量新增和批量修改的方式进行
(有一个问题,当你数据很多的时候,多到,你查询报错,记得建索引,加锁,哈哈哈)
(当然也有说使用原生jdbc,进行批处理+事务的,也可以试试)
7、存储过程操作,实现难度高(推荐gpt写,哈哈哈哈)
一种,数据进去了,再删除重复的
CREATE DEFINER=`root`@`localhost` PROCEDURE `proc1`()
BEGIN
DECLARE retainId VARCHAR(64);
DECLARE isAudit CHAR(1);
DECLARE cur CURSOR FOR
SELECT trade_no
FROM or_jackyun
GROUP BY trade_no, trade_type, trade_status
HAVING COUNT(trade_no) > 1;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
OPEN cur;
read_loop: LOOP
FETCH cur INTO trade_no_val;
IF done = 1 THEN
LEAVE read_loop;
END IF;
SELECT
is_audit
INTO
isAudit
FROM or_jackyun
WHERE trade_no = trade_no_val
ORDER BY trade_no, create_time ASC
LIMIT 1;
SELECT id INTO retainId FROM or_jackyun
WHERE trade_no = trade_no_val
ORDER BY trade_no, create_time DESC
LIMIT 1;
-- 更新数据
UPDATE or_jackyun
SET is_audit = isAudit
WHERE trade_no = trade_no_val
ORDER BY trade_no, create_time DESC
LIMIT 1;
-- 删除数据
DELETE FROM or_jackyun
WHERE trade_no = trade_no_val and id not IN (retainId);
END LOOP;
CLOSE cur;
END
一种,查询、新增、修改全部都放在里面一起
CREATE DEFINER=`root`@`localhost` PROCEDURE `BatchInsertOrUpdateTradeNos`(
IN trade_no_data JSON
)
BEGIN
DECLARE i INT DEFAULT 0;
DECLARE total_records INT;
-- 检查trade_no是否存在
DECLARE rows_affected INT;
-- 获取JSON数组中的记录总数
SET total_records = JSON_LENGTH(trade_no_data);
-- 循环处理每个记录
WHILE i < total_records DO
-- 获取JSON数据中的每个记录
SET @record = JSON_UNQUOTE(JSON_EXTRACT(trade_no_data, CONCAT('$[', i, ']')));
-- 解析JSON数据中的字段
SET @trade_no = JSON_UNQUOTE(JSON_EXTRACT(@record, '$.trade_no'));
SET @id = JSON_UNQUOTE(JSON_EXTRACT(@record, '$.id'));
SET @is_audit = JSON_UNQUOTE(JSON_EXTRACT(@record, '$.is_audit'));
SELECT COUNT(*) INTO rows_affected FROM or_jackyun WHERE trade_no = @trade_no;
IF rows_affected > 0 THEN
-- 存在,执行更新操作
UPDATE or_jackyun
SET
is_audit = @is_audit,
WHERE trade_no = @trade_no;
ELSE
-- 不存在,执行插入操作
INSERT INTO or_jackyun (id,trade_no, is_audit)
VALUES (@id,@trade_no, @is_audit);
END IF;
SET i = i + 1;
END WHILE;
END
调用存储过程(注意加@InterceptorIgnore(tenantLine = "1")注解)
@InterceptorIgnore(tenantLine = "1")
void callBatchInsertOrUpdateTradeNos(@Param("jsonData") String jsonData);
<!-- 调用存储过程的示例 -->
<select id="callBatchInsertOrUpdateTradeNos" statementType="CALLABLE">
{call BatchInsertOrUpdateTradeNos(
#{jsonData, mode=IN, jdbcType=VARCHAR, javaType=java.lang.String}
)}
</select>
8、别玩多线程了,慢慢单线程吧哈哈哈哈哈
感谢: