MyBatis中导致崩溃的主因是SQL超长或语法异常,而非集合长度本身;应通过主动分片(如每批500~1000项)、安全绑定(校验非空/类型/长度)、长度预控(匹配max_allowed_packet)及XML边界防护(包裹+open/close)来防范。

MyBatis 中用 <foreach></foreach> 构建 IN 查询或批量插入时,真正导致崩溃的不是“集合长度超限”本身,而是生成的 SQL 语句超出数据库能接受的最大包长(max_allowed_packet)或触发语法解析异常。MySQL 并没有 1000 个值的硬性限制,但单条 SQL 过长、参数过多、嵌套过深,确实会引发连接中断、语法错误甚至 OOM。防范的关键在于**主动分片 + 安全绑定 + 长度预控**,而不是等报错再拆。
按批次切分入参,避免单条 SQL 膨胀
把一个含上万 ID 的 List 拆成每批 500~1000 项的小集合,再逐批执行查询或插入。这不是“绕过限制”,而是匹配数据库实际承载能力(如 MySQL 默认 max_allowed_packet=4MB,一条含 5000 个整数的 IN 语句可能就超 2MB)。
- Java 层手动分片:用
subList()切割原始列表,生成List<list>></list>或List<list>></list> - MyBatis XML 中不直接遍历大集合,而是接收已分好的子集列表,用外层
<foreach></foreach>套内层<foreach></foreach>实现IN(...) OR IN(...) OR ...结构 - 示例逻辑:若传入
idList = [1,2,...,6500],则切成 7 个子列表(每组 1000),XML 中用collection="batchList"外循环,每个item是一个子列表,再内循环拼 IN
严格校验与清洗入参,杜绝非法内容注入
前端传来的 JSONArray 或字符串 ID 列表,未经校验直接进 <foreach></foreach>,可能带 null、空串、非数字、SQL 片段(如 "1' OR '1'='1"),导致语法错误或注入。不能依赖 MyBatis 的 #{} 绑定就万事大吉——它只防注入,不防类型错误和空值异常。
- Java 层解析 JSON 后,对每个元素做白名单校验:正则匹配纯数字 / UUID 格式,拒绝 null、空字符串、负数(如 ID 为 unsigned)、超长数值(如 > 19 位)
- 统一转为目标类型(如
Long.parseLong()),捕获NumberFormatException并快速失败,不传入 MyBatis - 限制总数量上限(如最多接收 10000 个 ID),超出则抛业务异常,不进入 DAO 层
选用合适执行模式,避免内存与网络瓶颈
<foreach></foreach> 拼接大批量 INSERT 是单条巨型 SQL,看似减少交互次数,实则易因 SQL 过长被拒、GC 压力大、回滚代价高。对数千以上数据,应切换为真批量模式。
立即学习“Java免费学习笔记(深入)”;
- 开启 MyBatis
ExecutorType.BATCH:Spring 中配置sqlSessionFactory.setExecutorType(ExecutorType.BATCH) - 配合 JDBC URL 加参数
rewriteBatchedStatements=true(MySQL 驱动特有),让驱动把多条 INSERT 合并为底层批量协议,而非拼 SQL - 每 500~1000 条调用一次
sqlSession.flushStatements(),及时推送并释放内存,避免堆溢出
XML 写法注意边界安全,规避隐藏字符与空集合
常见崩溃并非来自数据量,而是 XML 里看不见的空格、零宽字符、未处理的空集合。比如空 list 传入 <foreach></foreach> 且没设 open/close,会导致 WHERE id IN 语法错误;复制粘贴的标签自带不可见字符,也会让 XML 解析失败。
- IN 查询务必设置
open="("、close=")"、separator=",",并在外层加<if test="list != null and !list.isEmpty()"></if>包裹 - 手写 XML 时禁用富文本编辑器复制,用纯文本工具(如 VS Code)编写,开启显示空白字符功能检查
- 测试用例覆盖空集合、单元素、边界值(如 999/1001/5000 个元素),观察日志中最终生成的 SQL 是否合法


















