哈喽大家好,我是你们熟悉的31岁技术博主小米,一个爱写bug也爱修bug的程序员。

最近啊,我们团队在准备招一个资深Java后端的时候,我和同事们一起参与了几轮面试。说实话,现在的社招简历个个都“光鲜亮丽”,但真刀真枪问问题时,还是能看出谁是“纸老虎”,谁是真“硬核”。

今天就想和大家聊聊,在其中一场技术面试中,我们问到的一个很常见但也很容易答偏的问题:

“MyBatis如何执行批量操作?”

结果,10个人里有8个说的是用 foreach 实现多条 insert,还有人说:“我直接循环 insert 不就好了?”听得我差点从椅子上摔下来……

好了,话不多说,我们这就来还原下这个问题背后的故事,顺便也讲讲批量操作在实际业务中是怎么踩坑的。

故事开头:10W条数据插入,系统差点崩了

那是两年前的一个深夜,我和搭档阿强在处理一个“临时需求”——对接一个供应商的订单系统,导入对方历史订单数据到我们的数据库中,一共 10万条

阿强一开始用的是传统方式:

社招高频题:MyBatis怎么优雅搞定批量操作?_批量操作

起初没啥问题,可当他按下执行键不到5分钟,数据库 CPU 就飙到了95%,慢查询一堆,Tomcat还直接卡了两次。我们都懵了。

我: “哥们儿,你是循环调用 insert 啊?MyBatis 不支持批量的吗?”

阿强: “我也以为它自动会合并SQL来执行呢……”

好吧,阿强对不起,这锅我不能替你背。那天,我第一次系统地去翻了 MyBatis 的批量处理机制,才发现里面水还挺深!

误区澄清:MyBatis不是自动批量执行的!

很多人以为:

“我用 for 循环调用 mapper 的 insert(),MyBatis 会聪明地合并成批量 SQL。”

实际上并不会!

默认情况下,每一次调用 insert(),MyBatis 都会走一次完整的 SQL 执行流程:

  • 创建 PreparedStatement
  • 设置参数
  • 执行 SQL
  • 提交事务

一次一条,效率感人。

所以,MyBatis 里如果不做特别处理,循环插入就是 N 次独立 SQL 操作,性能极低。

那怎么才能实现真正的批量呢?继续往下看。

解法一:最常见的 foreach 拼 SQL

我们先来看第一个“半自动化”的方式,也是很多面试者第一反应:用 <foreach> 标签拼接批量 SQL。

XML 中的写法:

社招高频题:MyBatis怎么优雅搞定批量操作?_批量操作_02

这样,MyBatis 就会在一个 SQL 中拼接多组 values(),最后生成一个长 SQL,交由数据库一次性插入。

优点:

  • 简单直观,写起来快
  • 不依赖底层数据库或MyBatis配置

缺点:

  • SQL长度容易超限(数据库对单条SQL长度有限制)
  • MySQL 有的版本对 values() 个数也有限制(比如1000)
  • 批量更新不好支持

这个方案在 数据量中等(几百~几千) 的时候非常实用,也是我在项目中最常推荐的方式之一。

解法二:使用 SqlSession 的 ExecutorType.BATCH 模式

当你批量的数据量更大,比如 几万条甚至几十万条,怎么办?

这时候推荐你使用更“底层”的方式:显式创建 batch 模式的 SqlSession。

示例代码如下:

社招高频题:MyBatis怎么优雅搞定批量操作?_数据库_03

优点:

  • 真正意义上的批处理操作,性能提升明显
  • 控制更细粒度,适用于超大批次导入

缺点:

  • 代码稍显复杂
  • 不适合和 Spring 的声明式事务混用(除非集成MyBatis-Spring的BatchSession支持)
  • 异常处理要注意回滚

这是我们当时救命用的方案,后来 10W 条数据分批导入,耗时从十几分钟缩短到几十秒,系统再也没挂过。

解法三:MyBatis Plus 的批量方法

如果你项目中用的是 MyBatis Plus,那就更简单了。

社招高频题:MyBatis怎么优雅搞定批量操作?_SQL_04

MyBatis Plus 底层其实也是用了 <foreach> 或 Batch Executor 模式,只不过它帮你封装好了,默认是 1000 条一个批次。

想调整批次大小?

社招高频题:MyBatis怎么优雅搞定批量操作?_SQL_05

MyBatis Plus 可以说是“懒人专用”,写起来很爽,唯一的问题是你得接受它对 entity 的要求,比如继承 BaseMapper、使用注解等。

关于批量更新与删除

很多同学还会问:

“那批量更新怎么办?”

批量更新就没插入那么简单了,因为更新通常每一条的内容都不一样,SQL 不好拼。

最常见的写法是:

  • 使用 <foreach> 迭代 update
  • 或者写 CASE WHEN 的 SQL,复杂但效率高
  • 真正对性能要求特别高的,还可以考虑存储过程

示例(简单版):

社招高频题:MyBatis怎么优雅搞定批量操作?_SQL_06

但注意:大部分数据库并不支持多条 SQL 用分号连接执行!你需要配置 MyBatis 支持执行多语句,或用脚本方式执行。

小米碎碎念 & 面试建议

聊了这么多,回到面试场景,小米想说两点:

  • MyBatis 的批量操作,面试官考的是你对原理的掌握和实际项目的理解能力。别停留在 API 层面,要能讲出性能问题的原因和应对策略。
  • 批量操作从来不是“for循环 insert”这么简单,要考虑 SQL 语句构建、事务处理、缓存清理等多个因素。

真正的资深工程师,看到问题不慌,能根据场景选方案,才是加分项。

结语

好了,今天的技术故事分享到这,MyBatis 批量操作虽然是“老生常谈”,但却是项目中最容易踩坑的一块。写得简单,跑得慢;写得复杂,跑得快——这就是批处理的“魔法”。

如果你正在准备社招,不妨把今天这几个方案都撸一遍,别等到面试的时候说“我回去查查文档”哦~

我是小米,一个正在 debug 也正在成长的技术人,我们下期继续聊聊那些年我们踩过的 Java 社招坑吧!

END

如果你觉得有用,记得点个 在看 + 收藏 + 转发,让更多小伙伴不再掉坑!

我是小米,一个喜欢分享技术的31岁程序员。如果你喜欢我的文章,欢迎关注我的微信公众号“软件求生”,获取更多技术干货!

我们技术人,也要彼此照亮!