Mybatis解决以某个字段存在,批量更新,不存在批量插入(高效)(一)
背景
在开发企业级应用时,我们经常需要处理批量数据的插入和更新操作。传统的逐条处理方式性能低下,而简单的REPLACE INTO
或INSERT ... ON DUPLICATE KEY UPDATE
在某些场景下又不够灵活。本文将介绍一种基于临时表的高效批量插入/更新方案,解决复杂业务场景下的数据同步问题。
场景
这个表需要大量插入和更新数据,频繁的比对浪费时间,且效率不高,要减少数据库连接时间开销。可以采用临时表的方式进行插入更新。假设下表,根据username和age组合判断记录是否存在,存在则更新,不存在则插入。
整体设计逻辑
创建mapper接口
/*** 批量插入或更新(根据username和age判断)* @param list 批量数据列表* @return 影响的行数*/int batchInsertOrUpdateByUsernameAndAge(List<BatchTest> list);
创建mapper.xml
<insert id="batchInsertOrUpdateByUsernameAndAge" parameterType="java.util.List"><!-- 创建临时表存储批量数据 -->CREATE TEMPORARY TABLE temp_batch_test (username varchar(50),age int,email varchar(100),status tinyint) ENGINE=MEMORY;<!-- 插入数据到临时表 -->INSERT INTO temp_batch_test (username, age, email, status)VALUES<foreach collection="list" item="item" separator=",">(#{item.username}, #{item.age}, #{item.email}, #{item.status})</foreach>;<!-- 更新已存在的记录(匹配username和age) -->UPDATE batch_test bJOIN temp_batch_test t ON b.username = t.username AND b.age = t.ageSETb.email = t.email,b.status = t.status;<!-- 插入新记录(不存在的username和age组合) -->INSERT INTO batch_test (username, age, email, status)SELECT t.username, t.age, t.email, t.statusFROM temp_batch_test tLEFT JOIN batch_test b ON t.username = b.username AND t.age = b.ageWHERE b.username IS NULL;<!-- 删除临时表 -->DROP TEMPORARY TABLE IF EXISTS temp_batch_test;</insert>
更新和插入的逻辑
案例数据流程
初始数据(batch_test表)
id | username | age | status | |
---|---|---|---|---|
1 | 张三 | 25 | zhangsan@old.com | 1 |
2 | 李四 | 30 | lisi@old.com | 1 |
批量输入数据(temp_batch_test表)
username | age | status | |
---|---|---|---|
张三 | 25 | zhangsan@new.com | 0 |
李四 | 35 | lisi@new.com | 1 |
王五 | 28 | wangwu@new.com | 1 |
操作结果
-
更新操作:
-
匹配记录:张三(25岁)
-
执行:
UPDATE ... SET email='zhangsan@new.com', status=0
-
-
插入操作:
-
新记录:李四(35岁)、王五(28岁)
-
执行:
INSERT INTO ... VALUES ('李四',35,...), ('王五',28,...)
-
最终数据
id | username | age | status | ||
---|---|---|---|---|---|
1 | 张三 | 25 | zhangsan@new.com | 0 | ← 更新 |
2 | 李四 | 30 | lisi@old.com | 1 | |
3 | 李四 | 35 | lisi@new.com | 1 | ← 新增 |
4 | 王五 | 28 | wangwu@new.com | 1 | ← 新增 |
实现逻辑详解
核心逻辑步骤
-
临时表创建阶段
CREATE TEMPORARY TABLE temp_batch_test (username varchar(50),age int,email varchar(100),status tinyint ) ENGINE=MEMORY;
-
使用MEMORY引擎提高临时表操作速度
-
只包含必要字段,减少内存占用
-
-
数据加载阶段
INSERT INTO temp_batch_test VALUES ('张三',25,'zhangsan@new.com',0), ('李四',35,'lisi@new.com',1), ('王五',28,'wangwu@new.com',1);
-
使用MyBatis的foreach实现动态批插
-
参数化查询防止SQL注入
-
-
更新阶段
UPDATE batch_test b JOIN temp_batch_test t ON b.username = t.username AND b.age = t.age SET b.email = t.email, b.status = t.status;
-
插入阶段(重点)
这是插入操作的核心技术,通过 LEFT JOIN + IS NULL
实现:
FROM temp_batch_test t
LEFT JOIN batch_test b ON t.username = b.username AND t.age = b.age
WHERE b.username IS NULL
执行过程:
-
左连接:将临时表(t)与主表(b)按username和age进行连接
-
过滤:只保留主表中不存在的记录(即b.username为NULL的记录)
内存中的连接结果示例:
t.username | t.age | t.email | t.status | b.username | b.age | b.email | |
---|---|---|---|---|---|---|---|
张三 | 25 | zhangsan@new.com | 0 | 张三 | 25 | ... | 主表存在 |
李四 | 35 | lisi@new.com | 1 | NULL | NULL | NULL | 主表不存在 |
王五 | 28 | wangwu@new.com | 1 | NULL | NULL | NULL | 主表不存在 |
WHERE条件过滤后结果:
t.username | t.age | t.email | t.status |
---|---|---|---|
李四 | 35 | lisi@new.com | 1 |
王五 | 28 | wangwu@new.com | 1 |
执行批量插入
将过滤后的结果插入主表:
INSERT INTO batch_test (username, age, email, status) -- 上一步的查询结果
执行效果等价于:
INSERT INTO batch_test (username, age, email, status) VALUES ('李四', 35, 'lisi@new.com', 1), ('王五', 28, 'wangwu@new.com', 1);
关键技术点解析
-
反连接(Anti-Join)模式:
-
通过LEFT JOIN + IS NULL实现"不存在于"的逻辑
-
比NOT IN或NOT EXISTS性能更好,特别是大数据量时
-
-
复合条件判断:
ON t.username = b.username AND t.age = b.age
-
同时匹配username和age字段
-
只有当两个字段都相等时才认为是重复记录
-
-
NULL安全比较:
如果age可能为NULL,应该使用:ON t.username = b.username AND (t.age = b.age OR (t.age IS NULL AND b.age IS NULL))
-
批量插入优势:
-
单次SQL执行所有插入操作
-
比循环执行单条INSERT效率高10-100倍
-
减少网络往返和SQL解析开销
-
清理阶段
- 显式释放临时表资源
- 避免连接池复用时的表冲突
-
DROP TEMPORARY TABLE temp_batch_test;
实际执行案例
初始主表数据
id | username | age | status | 备注 | |
---|---|---|---|---|---|
1 | 张三 | 25 | zhangsan@old.com | 1 | |
2 | 李四 | 30 | lisi@old.com | 1 |
批量处理数据
username | age | status | 操作说明 | |
---|---|---|---|---|
张三 | 25 | zhangsan@new.com | 0 | 更新操作 |
李四 | 35 | lisi@new.com | 1 | 插入操作 |
王五 | 28 | wangwu@new.com | 1 | 插入操作 |
插入操作执行过程
-
临时表与主表LEFT JOIN中间结果:
临时表数据 主表匹配结果 张三(25) 匹配id=1的记录 李四(35) 无匹配(NULL) 王五(28) 无匹配(NULL) -
过滤后待插入数据:
username age email status 李四 35 lisi@new.com 1 王五 28 wangwu@new.com 1 -
最终主表数据:
id username age email status 操作说明 1 张三 25 zhangsan@new.com 0 被更新 2 李四 30 lisi@old.com 1 3 李四 35 lisi@new.com 1 新插入 4 王五 28 wangwu@new.com 1 新插入
性能优化建议
-
索引优化:
ALTER TABLE batch_test ADD INDEX `idx_username_age` (`username`, `age`);
-
批量大小控制:
-
建议每批500-1000条记录
-
过大的批次可能导致内存问题
-
-
临时表优化:
CREATE TEMPORARY TABLE ... (INDEX `idx_temp` (`username`, `age`) ) ENGINE=MEMORY;
-
服务器参数:
# my.cnf配置 tmp_table_size = 256M max_heap_table_size = 256M
这种插入机制通过巧妙的SQL设计,实现了高效、准确的批量数据插入,是处理数据同步场景的理想解决方案。
必要配置
properties文件
# application.properties
spring.datasource.url=jdbc:mysql://localhost:3306/dbname?allowMultiQueries=true
spring.datasource.hikari.connection-init-sql=SET SESSION sql_mode='NO_ENGINE_SUBSTITUTION'
测试数据:一定要自己动手试试
第一批数据(全部插入,status=1) - 20条
[{"username": "仇癸霖2", "age": 22, "email": "2244442", "status": 1},{"username": "靳浩然", "age": 33, "email": "hvbk3d38@vip.qq.com", "status": 1},{"username": "束雪", "age": 7, "email": "ssxtbf_ios@qq.com", "status": 1},{"username": "公孙雨", "age": 28, "email": "rain_gs@163.com", "status": 1},{"username": "欧阳明日", "age": 45, "email": "oymr@hotmail.com", "status": 1},{"username": "司马青", "age": 19, "email": "smqing@126.com", "status": 1},{"username": "令狐冲", "age": 32, "email": "linghuchong@gmail.com", "status": 1},{"username": "东方不败", "age": 40, "email": "dfbb@yeah.net", "status": 1},{"username": "西门吹雪", "age": 35, "email": "xmcx@sina.com", "status": 1},{"username": "慕容复", "age": 38, "email": "murongfu@qq.com", "status": 1},{"username": "赵灵儿", "age": 18, "email": "zle@163.com", "status": 1},{"username": "李逍遥", "age": 25, "email": "lxy@gmail.com", "status": 1},{"username": "林月如", "age": 22, "email": "lyr@126.com", "status": 1},{"username": "景天", "age": 30, "email": "jtian@qq.com", "status": 1},{"username": "唐雪见", "age": 27, "email": "txj@sina.com", "status": 1},{"username": "龙葵", "age": 20, "email": "lkui@163.com", "status": 1},{"username": "紫萱", "age": 300, "email": "zxuan@yeah.net", "status": 1},{"username": "徐长卿", "age": 35, "email": "xczq@hotmail.com", "status": 1},{"username": "重楼", "age": 500, "email": "chonglou@gmail.com", "status": 1},{"username": "花楹", "age": 15, "email": "huaying@qq.com", "status": 1}
]
第二批数据(混合更新和插入,更新status=0/新插入status=1) - 30条
[// 需要更新的记录(username+age与第一批重复){"username": "仇癸霖2", "age": 22, "email": "new_2244442", "status": 0},{"username": "靳浩然", "age": 33, "email": "new_hvbk3d38@vip.qq.com", "status": 0},{"username": "束雪", "age": 7, "email": "new_ssxtbf_ios@qq.com", "status": 0},{"username": "公孙雨", "age": 28, "email": "new_rain_gs@163.com", "status": 0},{"username": "欧阳明日", "age": 45, "email": "new_oymr@hotmail.com", "status": 0},// 新插入的记录{"username": "张无忌", "age": 28, "email": "zwj@mingjiao.org", "status": 1},{"username": "赵敏", "age": 25, "email": "zhaomin@yuandynasty.com", "status": 1},{"username": "周芷若", "age": 24, "email": "zzr@emei.org", "status": 1},{"username": "小昭", "age": 20, "email": "xiaozao@persia.com", "status": 1},{"username": "殷离", "age": 22, "email": "yinli@butterfly.com", "status": 1},{"username": "杨逍", "age": 40, "email": "yangxiao@mingjiao.org", "status": 1},{"username": "范遥", "age": 38, "email": "fanyao@mingjiao.org", "status": 1},{"username": "黛绮丝", "age": 42, "email": "daiqisi@persia.com", "status": 1},{"username": "谢逊", "age": 50, "email": "xiexun@lionking.com", "status": 1},{"username": "殷天正", "age": 60, "email": "yintianzheng@tiandihui.com", "status": 1},{"username": "韦一笑", "age": 45, "email": "weiyixiao@batman.com", "status": 1},{"username": "说不得", "age": 48, "email": "shuobude@monk.com", "status": 1},{"username": "冷谦", "age": 52, "email": "lengqian@cool.com", "status": 1},{"username": "彭莹玉", "age": 55, "email": "pengyingyu@pearl.com", "status": 1},{"username": "周颠", "age": 50, "email": "zhoudian@crazy.com", "status": 1},{"username": "铁冠道人", "age": 58, "email": "tieguandaoren@taoist.com", "status": 1},{"username": "朱元璋", "age": 35, "email": "zhuyuanzhang@emperor.com", "status": 1},{"username": "常遇春", "age": 38, "email": "changyuchun@general.com", "status": 1},{"username": "徐达", "age": 40, "email": "xuda@marshal.com", "status": 1},{"username": "汤和", "age": 42, "email": "tanghe@general.com", "status": 1},{"username": "邓愈", "age": 37, "email": "dengyu@general.com", "status": 1},{"username": "沐英", "age": 30, "email": "muying@general.com", "status": 1},{"username": "蓝玉", "age": 45, "email": "lanyu@general.com", "status": 1},{"username": "傅友德", "age": 50, "email": "fuyoude@general.com", "status": 1}
]
相关文章:

Mybatis解决以某个字段存在,批量更新,不存在批量插入(高效)(一)
背景 在开发企业级应用时,我们经常需要处理批量数据的插入和更新操作。传统的逐条处理方式性能低下,而简单的REPLACE INTO或INSERT ... ON DUPLICATE KEY UPDATE在某些场景下又不够灵活。本文将介绍一种基于临时表的高效批量插入/更新方案,解…...
高效C/C++之九:Coverity修复问题:关于数组操作 和 内存操作
【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了: 这一篇我们开始讲: 高效C/C之九:Coverity修复问题:关于数组操作 和 内存操作 目录 【关注我,后…...

【时时三省】(C语言基础)怎样定义和引用二维数组
山不在高,有仙则名。水不在深,有龙则灵。 ----CSDN 时时三省 有的问题需要用二维数组来处理。例如,有3个小分队,每队有6名队员,要把这些队员的工资用数组保存起来以备查。这就需要用到二维数组,如下图&…...
Ubuntu上安装MySQL 8并配置Navicat远程连接
1. 安装MySQL 8 sudo apt update sudo apt install mysql-server -y2. 启动MySQL服务 sudo systemctl start mysql sudo systemctl enable mysql3. 设置root密码 3.1 登录MySQL(默认无密码): sudo mysql3.2 修改root认证方式并设置密码&a…...

杨校老师竞赛课之C++备战蓝桥杯初级组省赛
目录 1. 灯塔 题目描述 输入描述 输出描述 输入样例1 输出样例1 输入样例2 输出样例2 数据说明 2. 子区间 题目描述 输入描述 输出描述 输入样例 输出样例 数据说明 3. 染色 题目描述 输入描述 输出描述 输入样例1 输出样例1 输入样例2 输出样例2 数据…...

Matlab 基于Hough变换的人眼虹膜定位方法
1、内容简介 Matlab220-基于Hough变换的人眼虹膜定位方法 可以交流、咨询、答疑 2、内容说明 略 3、仿真分析 略 4、参考论文 略...

vfrom表单设计器使用事件机制控制字段显示隐藏
1. 使用表单设计器进行debug调试 依据 vform3.0开发者文档 https://www.ganweicloud.com/docs/6.1.0/pages/d3e6d9/ 对switch组件设置事件逻辑 调试中...

【Redis篇】linux 7.6安装单机Redis7.0(参数优化详解)
💫《博主主页》: 🔎 CSDN主页 🔎 IF Club社区主页 🔥《擅长领域》:擅长阿里云AnalyticDB for MySQL(分布式数据仓库)、Oracle、MySQL、Linux、prometheus监控;并对SQLserver、NoSQL(MongoDB)有了…...

信号的概念及产生
信号的概念 信号(signal)是一种软件中断机制,用于通知进程发生了特定的事件。信号可以由系统、其他进程或进程自身发送。 在现实生活中,也有许多的信号,比如说:红绿灯、闹钟、上课铃、父母喊你回家吃饭等等…...

巧用python之--模仿PLC(PLC模拟器)
工作中用到了VM(VisionMaster4.3)有时候需要和PLC打交道,但是PLC毕竟是别人的,不方便修改别人的程序,这时候需要一个灵活的PLC模拟器是多么好呀! 先说背景: PLC型号 汇川Easy521: Modbus TCP 192.168.1.10:502 在汇川Easy521中Modbus保持寄存器D寄存器 ,在modbus协议中 0-4区…...

【计算机网络】用户从输入网址到网页显示,期间发生了什么?
1.URL解析 浏览器分解URL:https://www.example.com/page 协议:https域名:www.example.com路径:/page 2.DNS查询: 浏览器向DNS服务器发送查询请求,将域名解析为对应的IP地址。 3.CDN检查(如果有)&#…...
【计算机哲学故事1-3】默认设置:在有限的系统里,决定你想成为什么
她盯着屏幕上熟悉的蓝色窗户,语气里透着一丝无奈:“我发现,不管买多少次新电脑,开机那一刻,看到的永远是同一张桌面。” 我坐在她旁边,看着那台刚装好的电脑,笑了笑:“所以你在感慨…...
【嵌入式开发-UART】
嵌入式开发-UART ■ UART简介 ■ UART简介...

C++ 算法学习之旅:从入门到精通的秘籍
在编程的浩瀚宇宙中,C 算法宛如璀璨的星辰,照亮我们前行的道路。作为一名 C 算法小白,或许你和我一样,怀揣着对算法的好奇与憧憬,却又在学习的道路上感到迷茫。别担心,今天我就和大家分享一下如何学习各种基…...

计算机网络常识:缓存、长短连接 网络初探、URL、客户端与服务端、域名操作 tcp 三次握手 四次挥手
缓存: 缓存是对cpu,内存的一个节约:节约的是网络带宽资源 节约服务器的性能 资源的每次下载和请求都会造成服务器的一个压力 减少网络对资源拉取的延迟 这个就是浏览器缓存的一个好处 表示这个html页面的返回是不要缓存的 忽略缓存 需要每次…...

软件逆向工程核心技术:脱壳原理与实战分析
目录 一、脱壳技术概述:从保护到还原的逆向之旅 1.1 脱壳技术的本质与核心价值 1.2 壳的分类与核心技术解析 1.3 学习路径:从压缩壳到加密壳的渐进式突破 二、脱壳三步法:系统化逆向工程框架 2.1 核心流程总览 2.2 实战案例࿱…...
前端面经 作用域和作用域链
含义:JS中变量生效的区域 分类:全局作用域 或者 局部作用域 局部作用域:函数作用域 和 块级作用域ES6 全局作用域:在代码中任何地方都生效 函数中定义函数中生效,函数结束失效 块级作用域 使用let或const 声明 作用域链:JS查…...

华为OD机试真题——荒岛求生(2025A卷:200分)Java/python/JavaScript/C/C++/GO最佳实现
2025 A卷 200分 题型 本专栏内全部题目均提供Java、python、JavaScript、C、C、GO六种语言的最佳实现方式; 并且每种语言均涵盖详细的问题分析、解题思路、代码实现、代码详解、3个测试用例以及综合分析; 本文收录于专栏:《2025华为OD真题目录…...
【Python 字符串】
Python 中的字符串(str)是用于处理文本数据的基础类型,具有不可变性、丰富的内置方法和灵活的操作方式。以下是 Python 字符串的核心知识点: 一、基础特性 定义方式: s1 单引号字符串 s2 "双引号字符串" s…...
基础编程题目集 6-9 统计个位数字
本题要求实现一个函数,可统计任一整数中某个位数出现的次数。例如-21252中,2出现了3次,则该函数应该返回3。 函数接口定义: int Count_Digit ( const int N, const int D ); 其中N和D都是用户传入的参数。N的值不超过int的范围&…...
LeetCode[226] 翻转二叉树
思路: 使用递归,归根结底还是左右节点互相倒,那么肯定需要一个temp节点在中间传递,最后就是递归,没什么说的 代码: /*** Definition for a binary tree node.* public class TreeNode {* int …...

【CTFer成长之路】举足轻重的信息搜集
举足轻重的信息搜集 信息搜集 常见的搜集 题目描述: 一共3部分flag docker-compose.yml version: 3.2services:web:image: registry.cn-hangzhou.aliyuncs.com/n1book/web-information-backk:latestports:- 80:80启动方式 docker-compose up -d 题目Flag n1book{info_…...
AI——认知科学中的认知架构建立步骤与方法
认知科学中的认知架构建立步骤与方法 认知架构(Cognitive Architecture)是模拟人类心智活动的计算框架,旨在整合感知、记忆、推理、学习等核心认知功能。其建立需结合心理学理论、神经科学证据和计算建模技术。以下是建立认知架构的系统方法…...

Linux开发工具【中】
目录 一、vim 1.1 插入模式 1.2 底行模式 1)set nu 2)set nonu 3) /XXX n 4)!command 5)vs other 1.3 补充 1) 批量化操作 2)批量化替换 : 3)快速定位&am…...
Ceph PG unfound/lost 问题排查与解决
Ceph PG unfound/lost 问题排查与解决 背景现象排查过程经验总结参考命令结语 背景 Ceph 集群出现 HEALTH_ERR,提示有 PG 对象丢失(unfound),并且 repair 无法自动修复。 现象 ceph health detail 显示: HEALTH_ERR …...

MySQL OCP 认证限时免费活动 7 月 31 日 前截止!!!
为庆祝 MySQL 数据库发布 30 周年,Oracle 官方推出限时福利:2025 年 4 月 20 日至 7 月 31 日期间,所有人均可免费报考 MySQL OCP(Oracle Certified Professional)认证考试。该认证验证持证者在 MySQL 数据库管理、优化…...
Python Cookbook-7.8 使用 Berkeley DB 数据库
任务 你想将一些数据做持久化处理,而且也想体验一下BerkeleyDB数据库的简洁和高效。 解决方案 如果以前在你的计算机中安装过 BerkeleyDB,Python标准库附带的bsddb包(以及可选的 bsddb3,用于访间Berkeley DBrelease 3.2数据库)可以被用来作…...
STM32智能手表:基于FreeRTOS
引言 随着物联网和可穿戴设备的快速发展,智能手表作为典型代表,集成了传感器数据采集、实时显示、无线通信等多项功能。本文将深入剖析一个基于STM32和FreeRTOS的智能手表项目,从硬件架构到软件设计,逐步讲解如何构建一个完整的嵌…...

学习笔记:数据库——事务
1.内容: 基于现有数据库设计检查点实验,观察比较提交前后执行结果并分析。 2.实现 源码 -- 开启事务 START TRANSACTION;-- 插入一条订单记录(客户ID为10002) INSERT INTO orders (o_date, c_id) VALUES (NOW(), 10002);-- 获…...
C++命名空间、内联与捕获
命名空间namespace 最常见的命名空间是std,你一定非常熟悉,也就是: using namespace std;命名空间的基本格式 注意,要在头文件里面定义! namespace namespace_name{data_type function_name(data_type parameter){data_type result;//function contentreturn result;}…...