Mybatis解决以某个字段存在,批量更新,不存在批量插入(高效)(一)

news2025/5/9 15:29:28

背景

在开发企业级应用时,我们经常需要处理批量数据的插入和更新操作。传统的逐条处理方式性能低下,而简单的REPLACE INTOINSERT ... 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 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;

        <!-- 插入新记录(不存在的username和age组合) -->
        INSERT INTO batch_test (username, age, email, status)
        SELECT t.username, t.age, t.email, t.status
        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;

        <!-- 删除临时表 -->
        DROP TEMPORARY TABLE IF EXISTS temp_batch_test;
    </insert>

更新和插入的逻辑

案例数据流程

初始数据(batch_test表)

idusernameageemailstatus
1张三25zhangsan@old.com1
2李四30lisi@old.com1

批量输入数据(temp_batch_test表)

usernameageemailstatus
张三25zhangsan@new.com0
李四35lisi@new.com1
王五28wangwu@new.com1

操作结果

  1. 更新操作

    • 匹配记录:张三(25岁)

    • 执行:UPDATE ... SET email='zhangsan@new.com', status=0

  2. 插入操作

    • 新记录:李四(35岁)、王五(28岁)

    • 执行:INSERT INTO ... VALUES ('李四',35,...), ('王五',28,...)

最终数据

idusernameageemailstatus
1张三25zhangsan@new.com0← 更新
2李四30lisi@old.com1
3李四35lisi@new.com1← 新增
4王五28wangwu@new.com1← 新增

实现逻辑详解

核心逻辑步骤

  1. 临时表创建阶段
    CREATE TEMPORARY TABLE temp_batch_test (
      username varchar(50),
      age int,
      email varchar(100),
      status tinyint
    ) ENGINE=MEMORY;
    • 使用MEMORY引擎提高临时表操作速度

    • 只包含必要字段,减少内存占用

  2. 数据加载阶段
    INSERT INTO temp_batch_test VALUES
    ('张三',25,'zhangsan@new.com',0),
    ('李四',35,'lisi@new.com',1),
    ('王五',28,'wangwu@new.com',1);
    • 使用MyBatis的foreach实现动态批插

    • 参数化查询防止SQL注入

  3. 更新阶段
    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;
  4. 插入阶段(重点)

这是插入操作的核心技术,通过 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

执行过程:

  1. 左连接:将临时表(t)与主表(b)按username和age进行连接

  2. 过滤:只保留主表中不存在的记录(即b.username为NULL的记录)

内存中的连接结果示例:

t.usernamet.aget.emailt.statusb.usernameb.ageb.email
张三25zhangsan@new.com0张三25...主表存在
李四35lisi@new.com1NULLNULLNULL主表不存在
王五28wangwu@new.com1NULLNULLNULL 主表不存在

WHERE条件过滤后结果:

t.usernamet.aget.emailt.status
李四35lisi@new.com1
王五28wangwu@new.com1
执行批量插入

将过滤后的结果插入主表:

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);

关键技术点解析

  1. 反连接(Anti-Join)模式

    • 通过LEFT JOIN + IS NULL实现"不存在于"的逻辑

    • 比NOT IN或NOT EXISTS性能更好,特别是大数据量时

  2. 复合条件判断

    ON t.username = b.username AND t.age = b.age
    • 同时匹配username和age字段

    • 只有当两个字段都相等时才认为是重复记录

  3. NULL安全比较
    如果age可能为NULL,应该使用:

    ON t.username = b.username 
    AND (t.age = b.age OR (t.age IS NULL AND b.age IS NULL))
  4. 批量插入优势

    • 单次SQL执行所有插入操作

    • 比循环执行单条INSERT效率高10-100倍

    • 减少网络往返和SQL解析开销

   清理阶段
  1. 显式释放临时表资源
  2. 避免连接池复用时的表冲突
  3. DROP TEMPORARY TABLE temp_batch_test;

实际执行案例

初始主表数据

idusernameageemailstatus备注
1张三25zhangsan@old.com1
2李四30lisi@old.com1

批量处理数据

usernameageemailstatus操作说明
张三25zhangsan@new.com0更新操作
李四35lisi@new.com1插入操作
王五28wangwu@new.com1插入操作

插入操作执行过程

  1. 临时表与主表LEFT JOIN中间结果:

    临时表数据主表匹配结果
    张三(25)匹配id=1的记录
    李四(35)无匹配(NULL)
    王五(28)无匹配(NULL)
  2. 过滤后待插入数据:

    usernameageemailstatus
    李四35lisi@new.com1
    王五28wangwu@new.com1
  3. 最终主表数据:

    idusernameageemailstatus操作说明
    1张三25zhangsan@new.com0被更新
    2李四30lisi@old.com1
    3李四35lisi@new.com1新插入
    4王五28wangwu@new.com1新插入

性能优化建议

  1. 索引优化

    ALTER TABLE batch_test 
    ADD INDEX `idx_username_age` (`username`, `age`);
  2. 批量大小控制

    • 建议每批500-1000条记录

    • 过大的批次可能导致内存问题

  3. 临时表优化

    CREATE TEMPORARY TABLE ... (
      INDEX `idx_temp` (`username`, `age`)
    ) ENGINE=MEMORY;
  4. 服务器参数

    # 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}
]

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2371580.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【时时三省】(C语言基础)怎样定义和引用二维数组

山不在高&#xff0c;有仙则名。水不在深&#xff0c;有龙则灵。 ----CSDN 时时三省 有的问题需要用二维数组来处理。例如&#xff0c;有3个小分队&#xff0c;每队有6名队员&#xff0c;要把这些队员的工资用数组保存起来以备查。这就需要用到二维数组&#xff0c;如下图&…

杨校老师竞赛课之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(参数优化详解)

&#x1f4ab;《博主主页》&#xff1a; &#x1f50e; CSDN主页 &#x1f50e; IF Club社区主页 &#x1f525;《擅长领域》&#xff1a;擅长阿里云AnalyticDB for MySQL(分布式数据仓库)、Oracle、MySQL、Linux、prometheus监控&#xff1b;并对SQLserver、NoSQL(MongoDB)有了…

信号的概念及产生

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

巧用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&#xff1a;https://www.example.com/page 协议&#xff1a;https域名&#xff1a;www.example.com路径&#xff1a;/page 2.DNS查询&#xff1a; 浏览器向DNS服务器发送查询请求&#xff0c;将域名解析为对应的IP地址。 3.CDN检查(如果有)&#…

C++ 算法学习之旅:从入门到精通的秘籍

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

计算机网络常识:缓存、长短连接 网络初探、URL、客户端与服务端、域名操作 tcp 三次握手 四次挥手

缓存&#xff1a; 缓存是对cpu&#xff0c;内存的一个节约&#xff1a;节约的是网络带宽资源 节约服务器的性能 资源的每次下载和请求都会造成服务器的一个压力 减少网络对资源拉取的延迟 这个就是浏览器缓存的一个好处 表示这个html页面的返回是不要缓存的 忽略缓存 需要每次…

软件逆向工程核心技术:脱壳原理与实战分析

目录 一、脱壳技术概述&#xff1a;从保护到还原的逆向之旅 1.1 脱壳技术的本质与核心价值 1.2 壳的分类与核心技术解析 1.3 学习路径&#xff1a;从压缩壳到加密壳的渐进式突破 二、脱壳三步法&#xff1a;系统化逆向工程框架 2.1 核心流程总览 2.2 实战案例&#xff1…

华为OD机试真题——荒岛求生(2025A卷:200分)Java/python/JavaScript/C/C++/GO最佳实现

2025 A卷 200分 题型 本专栏内全部题目均提供Java、python、JavaScript、C、C、GO六种语言的最佳实现方式&#xff1b; 并且每种语言均涵盖详细的问题分析、解题思路、代码实现、代码详解、3个测试用例以及综合分析&#xff1b; 本文收录于专栏&#xff1a;《2025华为OD真题目录…

【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_…

Linux开发工具【中】

目录 一、vim 1.1 插入模式 1.2 底行模式 1&#xff09;set nu 2&#xff09;set nonu 3&#xff09; /XXX n 4&#xff09;&#xff01;command 5&#xff09;vs other 1.3 补充 1&#xff09; 批量化操作 2&#xff09;批量化替换 : 3&#xff09;快速定位&am…

MySQL OCP 认证限时免费活动​ 7 月 31 日 前截止!!!

为庆祝 MySQL 数据库发布 30 周年&#xff0c;Oracle 官方推出限时福利&#xff1a;2025 年 4 月 20 日至 7 月 31 日期间&#xff0c;所有人均可免费报考 MySQL OCP&#xff08;Oracle Certified Professional&#xff09;认证考试。该认证验证持证者在 MySQL 数据库管理、优化…

学习笔记:数据库——事务

1.内容&#xff1a; 基于现有数据库设计检查点实验&#xff0c;观察比较提交前后执行结果并分析。 2.实现 源码 -- 开启事务 START TRANSACTION;-- 插入一条订单记录&#xff08;客户ID为10002&#xff09; INSERT INTO orders (o_date, c_id) VALUES (NOW(), 10002);-- 获…

UE5 Daz头发转Blender曲线再导出ABC成为Groom

先安装Daz to Blender Import插件 【神器】 --DAZ一键导入blender插件的详细安装和使用&#xff0c;自带骨骼绑定和控制器&#xff0c;多姿势动画&#xff0c;Importer桥接插件_哔哩哔哩_bilibili 然后安装DAZHairConverter插件 一分钟将DAZ头发转化成Blender粒子毛发_哔哩哔…

【贪心算法】贪心算法四

贪心算法四 1.最长回文串2.增减字符串匹配3.分发饼干4.最优除法点赞👍👍收藏🌟🌟关注💖💖 你的支持是对我最大的鼓励,我们一起努力吧!😃😃 1.最长回文串 题目链接: 409. 最长回文串 题目分析: 给一个包含大小字母的字符串,从里面挑选出来一些字母构成一个…

【漫话机器学习系列】240.真正类率(True Positive Rate,TPR)

理解真正类率&#xff08;True Positive Rate&#xff0c;TPR&#xff09;&#xff1a;公式、意义与应用 在机器学习与深度学习模型评估中&#xff0c;"真正类率"&#xff08;True Positive Rate&#xff0c;简称TPR&#xff09;是一个非常重要的指标。TPR反映了分类…

Linux的基础开发工具

目录 前言&#xff1a; 1、包管理器yum 1.1 软件包的依赖 1.2 镜像源 1.3 查找/安装/卸载软件 2、编辑器vim 2.1 命令模式(默认) 2.1.1 撤销与反撤销 2.1.2 光标定位 2.1.3 复制&&剪切(删除)&&粘贴 2.1.4 替换 2.1.5 插入模式 2.1.6 V-Block模式 …