MyBatis 动态 SQL 与批量插入优化:从循环单条到一条 SQL 插万行 MyBatis 动态 SQL 与批量插入优化:从循环单条到一条 SQL 插万行用 MyBatis 写查询,条件一多就开始拼字符串,SQL 里全是11这种脏东西;做批量插入时又习惯性 for 循环调 mapper,一万条数据插了半分钟。这两个痛点其实都有干净的解法。这篇把动态 SQL 的四个核心标签讲透,再一步步把批量插入从「每秒几百条」优化到「秒级万行」,代码都能直接跑。动态 SQL:告别where 11先看最原始的拼接写法,很多老项目里到处都是:!-- 反面教材:靠 11 兜底,SQL 脏且易错 --selectidsearchresultTypeUserselect * from user where 11iftestname ! nulland name #{name}/ififteststatus ! nulland status #{status}/if/selectwhere 11是为了解决「第一个条件前面要不要加 and」的问题,但它让 SQL 变脏,还可能让优化器误判。MyBatis 的where标签专门解决这个:它会自动加上 where,并智能去掉紧跟其后的第一个 and/or。selectidsearchresultTypeUserselect * from userwhereiftestname ! null and name ! and name #{name}/ififteststatus ! nulland status #{status}/if/where/select如果所有条件都不成立,where连 where 关键字都不会输出,SQL 依然合法。同理,更新语句用set标签自动处理末尾多余的逗号:updateidupdateupdate usersetiftestname ! nullname #{name},/ififteststatus ! nullstatus #{status},/if/setwhere id #{id}/updateset会自动去掉最后一个字段后面多余的逗号,你只管每行都写逗号就行。foreach:IN 查询与批量的基础foreach用来遍历集合,最常见的是IN查询:selectidfindByIdsresultTypeUserselect * from user where id inforeachcollectionidsitemidopen(separator,close)#{id}/foreach/selectopen/close是首尾包裹,separator是元素分隔符。生成的 SQL 是where id in (?, ?, ?)。坑点一:集合为空会生成in ()语法错误。调用前一定要判空:if(idsnull||ids.isEmpty()){returnCollections.emptyList();// 空集合直接返回,别进 SQL}坑点二:IN 列表别太长。几千个 id 塞进 IN 会让 SQL 巨长、优化器变慢,MySQL 还有max_allowed_packet限制。超过几百个建议分批查。批量插入:从 for 循环到一条 SQL最慢的写法:for 循环单条插入// 反面教材:一万次网络往返,极慢for(Useru:users){userMapper.insert(u);// 每条一次 SQL、一次网络往返}一万条就是一万次数据库往返,每次都有网络和 SQL 解析开销,慢到无法接受。用 foreach 拼成一条 INSERT把多行 values 拼进一条 SQL,一次往返插完:insertidbatchInsertinsert into user (name, status) valuesforeachcollectionusersitemuseparator,(#{u.name}, #{u.status})/foreach/insert对应 mapper 接口:intbatchInsert(Param(users)ListUserusers);生成的 SQL 形如insert into user (name, status) values (?,?),(?,?),(?,?)...。一万条从几十秒降到几百毫秒,这是最立竿见影的一步。但这个方案有上限:拼出来的 SQL 长度受 MySQLmax_allowed_packet(默认 4MB/16MB)限制,几万行、字段又多时会超包报错。所以要分批,每批 500~1000 条:publicvoidbatchInsertInChunks(ListUserusers){intbatchSize1000;for(inti0;iusers.size();ibatchSize){ListUserchunkusers.subList(i,Math.min(ibatchSize,users.size()));userMapper.batchInsert(chunk);// 每批一条 SQL}}数据量极大时:用 ExecutorType.BATCH当数据量到十万级、或者要批量 update/delete 时,更稳的是 MyBatis 的批处理执行器。它复用同一个PreparedStatement,把多次操作攒到一起 flush,底层走 JDBC 的addBatch:publicvoidbatchInsertWithExecutor(ListUserusers,SqlSessionFactoryfactory){// 关键:openSession(ExecutorType.BATCH) 开启批处理模式try(SqlSessionsessionfactory.openSession(ExecutorType.BATCH)){UserMappermappersession.getMapper(UserMapper.class);inti0;for(Useru:users){mapper.insert(u);// 这里仍是单条 insert 语句,但不会立即执行if(i%10000){session.flushStatements();// 每 1000 条 flush 一次,避免攒太多爆内存}}session.flushStatements();// 提交剩余的session.commit();}}它和 foreach 拼 SQL 的区别:foreach 是「一条超长 SQL」,BATCH 是「很多条短 SQL 一次性发送」。BATCH 模式对超大数据量更友好,不受单条 SQL 长度限制,还能统一用于 insert/update/delete。用 MySQL 时还要在连接串加上rewriteBatchedStatementstrue,否则 JDBC 的 addBatch 对 insert 不会真正合并,优化打折:jdbc:mysql://host:3306/db?rewriteBatchedStatementstrue加了这个参数,驱动会把多条 insert 重写成一条多值 insert,批处理才真正生效——这是最容易被忽略、却决定性能的一行。三种方案怎么选几十到几百条:foreach 拼一条 INSERT,简单直接。几千到几万条:foreach 分批(每批 1000),兼顾速度和 packet 限制。十万级以上、或要批量 update:ExecutorType.BATCHrewriteBatchedStatementstrue,最稳。小结动态 SQL 用where替代where 11,用set处理更新的逗号,SQL 干净不易错。foreach做 IN 查询,务必判空集合(避免in ())、控制列表长度。批量插入三级优化:for 单条(最慢)→ foreach 拼一条 SQL(快,但受 packet 限制要分批)→ExecutorType.BATCH(超大数据量最稳)。MySQL 用批处理必须在连接串加rewriteBatchedStatementstrue,否则 addBatch 不合并,白优化。一句话记忆点:别在 Java for 循环里调 insert;小批量 foreach 拼 SQL,大批量 BATCH 执行器加 rewriteBatchedStatements。

相关新闻

最新新闻

SerenityOS 命令行选项解析指南:getopt 与 getopt_long 用法、返回值与底层实现

SerenityOS 命令行选项解析指南:getopt 与 getopt_long 用法、返回值与底层实现

SerenityOS 命令行选项解析指南:getopt 与 getopt_long 用法、返回值与底层实现 【免费下载链接】serenity The Serenity Operating System 🐞 项目地址: https://gitcode.com/GitHub_Trending/se/serenity 导读 本文以 getopt(3) 手册 为核心&a…

2026/9/26 23:24:47
轻量服务器还是ECS?大促云服务器选购与避坑实战指南

轻量服务器还是ECS?大促云服务器选购与避坑实战指南

每年大促节点,群里永远有人在问同一个问题:“38元的轻量服务器到底怎么抢?为什么我每次点进去都是已售罄?68元直购和99元的ECS我到底选哪个?”作为一个常年帮团队和自己采购云服务器的老用户,我太清楚这种纠…

2026/9/26 18:48:15
为 AI 代理的 Review 动作编写 Cedar 审批门控策略:review-agent-governance 策略编写实战指南

为 AI 代理的 Review 动作编写 Cedar 审批门控策略:review-agent-governance 策略编写实战指南

为 AI 代理的 Review 动作编写 Cedar 审批门控策略:review-agent-governance 策略编写实战指南 【免费下载链接】agents Multi-harness agentic plugin marketplace for Claude Code, Codex, Cursor, OpenCode, GitHub Copilot, and Google Antigravity 项目地址:…

2026/9/26 3:42:08
PaddleOCR 手写数学公式识别算法 CAN 实战指南:Counting-Aware Network 训练、评估与推理部署

PaddleOCR 手写数学公式识别算法 CAN 实战指南:Counting-Aware Network 训练、评估与推理部署

PaddleOCR 手写数学公式识别算法 CAN 实战指南:Counting-Aware Network 训练、评估与推理部署 【免费下载链接】PaddleOCR Turn any PDF or image document into structured data for your AI. A powerful, lightweight OCR toolkit that bridges the gap between i…

2026/9/26 11:37:29
Spring源码解析:构造器注入的类型转换与候选匹配机制

Spring源码解析:构造器注入的类型转换与候选匹配机制

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/26 4:08:27
openai-agents-python 多模型接入指南:深入解析 AnyLLMModel 适配层与 any-llm 路由

openai-agents-python 多模型接入指南:深入解析 AnyLLMModel 适配层与 any-llm 路由

openai-agents-python 多模型接入指南:深入解析 AnyLLMModel 适配层与 any-llm 路由 【免费下载链接】openai-agents-python A lightweight, powerful framework for multi-agent workflows 项目地址: https://gitcode.com/GitHub_Trending/op/openai-agents-pyth…

2026/9/26 21:11:24

日新闻

周新闻