当前位置: 首页 > news >正文

用 MyBatis + MySQL 实现高效的批量 Upsert

在日常开发中,我们经常遇到这样的场景:需要将一个来自外部 API 或文件的数据列表,同步到我们的数据库中。这个列表里,既有需要新增的记录,也有需要更新的记录。接下来介绍一种方法,可以只用一次数据库交互,就完成这所有的新增和更新操作。

核心:MySQL 的 INSERT ... ON DUPLICATE KEY UPDATE

INSERT INTO table_name (column1, column2, ...)
VALUES (value1, value2, ...);
ON DUPLICATE KEY UPDATEcolumn1 = new_value1,column2 = new_value2,...;

工作原理

  1. 它首先尝试执行 INSERT 操作。

  2. 如果 INSERT 因为主键唯一索引冲突而失败,它不会报错

  3. 相反,它会立即跳转去执行 ON DUPLICATE KEY UPDATE 后面的 UPDATE 逻辑,对那条已存在的记录进行更新。

为了在 UPDATE 部分引用到 VALUES 中的新值,MySQL 提供了一个特殊的 VALUES(column_name) 函数。

结合 MyBatis:实现批量自动化

单独的 UPSERT 语句只能处理一条数据,要实现批量处理,就需要借助 MyBatis 强大的动态 SQL 功能,特别是 <foreach> 标签。下面,我们来构建一个通用的、批量的 Upsert Mapper 方法。

1. Mapper 接口定义

首先,在MyBatis Mapper 接口中定义一个接收 List 的方法:

// DataProductMapper.java
public interface DataProductMapper {/*** 批量更新或插入数据产品* @param productList 需要同步的数据产品列表* @return 影响的行数*/int batchUpsert(List<DataProduct> productList);
}
2. Mapper XML 实现 (核心代码)

在对应的 DataProductMapper.xml 文件中,来编写sql语句

<insert id="batchUpsert" parameterType="java.util.List"><!-- 1. INSERT 部分:定义要操作的表和所有字段 -->INSERT INTO data_product (product_id, supplier_subject_name, product_name, product_type, product_desc, ...)<!-- 2. VALUES 部分:使用 <foreach> 动态生成多组值 -->VALUES<foreach collection="list" item="item" separator=",">(#{item.productId}, #{item.supplierSubjectName}, #{item.productName},#{item.productType}, #{item.productDesc}, ...)</foreach><!-- 3. ON DUPLICATE KEY UPDATE 部分:定义冲突时的更新规则 -->ON DUPLICATE KEY UPDATEsupplier_subject_name = VALUES(supplier_subject_name),product_name = VALUES(product_name),product_type = VALUES(product_type),product_desc = VALUES(product_desc),...
</insert>
  • <insert> 标签: 虽然我们的功能包含 UPDATE,但因为 SQL 语句以 INSERT 开头,所以必须使用 <insert> 标签。

  • <foreach collection="list" ...>: 这是实现批量的关键。它会遍历你传入的 productList(在MyBatis中默认名为 list),并为列表中的每一个 item(DataProduct 对象),都生成一组 (...) 的值。

  • separator=",": 在每组值之间自动加上逗号,完美地构造出 VALUES (...), (...), (...) 的标准多行插入语法。

  • ON DUPLICATE KEY UPDATE ...: 这是整个 Upsert 逻辑的核心。

  • column = VALUES(column): 这句的意思是:“如果发生主键冲突,请将数据库中已存在记录的 column 字段,更新为我在 VALUES 部分为这条记录提供的那个新值。”

总结

  1. 性能极致:将成百上千次数据库交互,压缩为一次。这是数量级的性能提升。

  2. 代码简洁:将复杂的业务层判断逻辑,简化为一行 Mapper 调用,极大提升了代码的可读性和可维护性。

  3. 数据一致性:整个操作在数据库层面被视为一个单一的语句,具有更好的原子性保障

http://www.dtcms.com/a/308196.html

相关文章:

  • 关于tresos Studio(EB)的MCAL配置之GtmCfg
  • 性能测试篇 :Jmeter监控服务器性能
  • Golang 语言的编程技巧之类型
  • 基础组件(六):网络缓冲区设计 和 定时器方案
  • TTS语音合成|GPT-SoVITS语音合成服务器部署,实现http访问
  • Vue3+Vite项目如何简单使用tsx
  • nl2sql grpo强化学习训练,加大数据量和轮数后,准确率没提升,反而下降了,如何调整
  • PostgreSQL dblink 与 Spring Boot @Transactional 的事务整合
  • Text2SQL 智能问答系统开发-预定义模板(二)
  • docker离线安装mysql镜像
  • 记录几个SystemVerilog的语法——覆盖率
  • 基于MATLAB的GUI来对不同的(彩色或灰色)图像进行图像增强
  • 【国内电子数据取证厂商龙信科技】内存取证
  • 法式基因音响品牌SK(SINGKING AUDIO)如何以硬核科技重塑专业音频版图
  • 防御保护第一次作业
  • AI Gateway 分析:OpenRouter vs Higress
  • python基础语法3,组合数据类型(简单易上手的python语法教学)(课后习题)
  • BFT平台:打造科研教育“最强机器人矩阵”
  • 自动驾驶控制算法——PID算法
  • 蓝桥杯----DS18B20温度传感器
  • vue3+arcgisAPI4示例:轨迹点模拟移动(附源码下载)
  • InfluxDB 与 Python 框架结合:Django 应用案例(二)
  • ASIC芯片简介
  • LangGraph认知篇-Send机制
  • TypeScript 基础介绍(二)
  • QT6 Python UI文件转换PY文件的方法
  • 如何为C#加入EPPlus 包
  • 【Flask基础②】 | 路由、响应与异常处理
  • 微服务快速集成 TraceId
  • 企业智脑1.3.2版本发布,设备管理+智能体OS双核驱动,重构数字生产力边界