题目
MyBatis动态SQL中如何安全处理IN查询并防止SQL注入?
信息
- 类型:问答
- 难度:⭐⭐
考点
动态SQL编写,SQL注入防护,MyBatis参数处理
快速回答
安全处理IN查询的核心要点:
- 使用
<foreach>标签动态生成占位符 - 必须使用
#{item}预编译占位符而非${item}拼接 - 对集合参数进行非空校验
- 当集合为空时提供fallback方案(如返回空结果)
1. 问题背景与原理说明
在MyBatis处理WHERE column IN (value1, value2)查询时,直接拼接字符串会导致:
- SQL注入风险:使用
${param}拼接用户输入 - 语法错误:集合为空时生成
IN ()非法语句 - 性能问题:未利用预编译优势
MyBatis通过OGNL表达式解析动态SQL标签,#{}会被转换为JDBC预编译占位符(?),而${}直接文本替换。
2. 正确代码示例
<select id="selectUsersByIds" resultType="User">
SELECT * FROM users
WHERE id IN
<foreach item="id" collection="idList"
open="(" separator="," close=")">
#{id} <!-- 关键:使用#{}预编译 -->
</foreach>
<if test="idList == null or idList.isEmpty()">
AND 1=0 <!-- 集合为空时返回空结果 -->
</if>
</select>3. 最佳实践
- 参数校验:在Mapper接口方法中验证集合非空
List<User> selectUsersByIds(@Param("idList") List<Long> idList); - 防御空集合:
<if>标签处理空集合,避免生成非法SQL - 使用命名参数:
@Param明确指定集合名称 - 大小限制:对IN列表长度做业务层限制(如最大1000项)
4. 常见错误
- 错误1:使用${}拼接
<foreach>${id}</foreach>→ 导致SQL注入 - 错误2:省略空集合处理
未处理idList.isEmpty()→ 执行时报语法错误 - 错误3:错误嵌套引号
IN ('<foreach>...</foreach>')→ 破坏预编译结构
5. 扩展知识
- 批量插入优化:同样原理实现安全批量插入
<insert id="batchInsert"> INSERT INTO table(field) VALUES <foreach item="item" collection="list" separator=","> (#{item.value}) </foreach> </insert> - 动态SQL替代方案:
- 注解方式使用
@SelectProvider动态生成SQL - MyBatis-Plus的
QueryWrapper.in()方法
- 注解方式使用
- 预编译原理:数据库先接收带占位符的SQL模板,再接收参数值执行,从根本上隔离指令与数据