mybatis 单值或多值传参存在风险举例分析
代码如下,存在风险
<if test="budgetYear != null and budgetYear != ''">AND budget_year = #{budgetYear}</if><if test="budgetYears != null">AND budget_year IN<foreach open="(" close=")" collection="budgetYears" index="no" item="budgetYear" separator=",">#{budgetYear, jdbcType=VARCHAR}</foreach></if>
分析如下
这段 MyBatis 动态 SQL 代码可能存在以下几个潜在的 Bug 或优化点:
1. 数组/集合空值判断不严谨(严重问题)
问题点:
<if test="budgetYears != null">
只检查了是否为 null,没有检查空数组/空集合
风险:
如果传入空数组 budgetYears = []
,会生成错误 SQL:budget_year IN ()
修复建议:
xml
复制
<if test="budgetYears != null and budgetYears.length > 0"> <!-- 如果是集合则用 !budgetYears.isEmpty() -->
运行 HTML
2. 参数名冲突(逻辑问题)
问题点:
item="budgetYear"
与外部参数 budgetYear
同名
风险:
在 <foreach>
内部使用时可能产生歧义(取决于 MyBatis 版本)
修复建议:
xml
复制
<foreach ... item="yearItem"> <!-- 改为不同名 -->#{yearItem, jdbcType=VARCHAR} </foreach>
运行 HTML
3. 条件互斥缺失(业务逻辑问题)
问题点:
budgetYear
和 budgetYears
两个条件可能同时生效
风险:
如果同时传入 budgetYear=2023
和 budgetYears=[2024]
,会导致矛盾条件:
sql
复制
WHERE budget_year = 2023 AND budget_year IN (2024) -- 永远无结果
修复建议:
改用 <choose>
结构确保互斥:
xml
复制
<choose><when test="budgetYear != null and budgetYear != ''">AND budget_year = #{budgetYear}</when><when test="budgetYears != null and budgetYears.length > 0">AND budget_year IN (...)</when> </choose>
运行 HTML
4. 缺少类型安全校验(潜在问题)
问题点:
没有验证 budgetYears
数组元素的有效性
风险:
如果数组包含 null
值(如 ["2023", null]
),可能引发异常
修复建议:
xml
复制
<foreach ...><!-- 添加 javaType 确保类型安全 -->#{yearItem, jdbcType=VARCHAR, javaType=string} </foreach>
运行 HTML
5. 索引参数未使用(冗余代码)
问题点:
定义了 index="no"
但未实际使用
修复建议:
直接移除(除非需要索引值):
xml
复制
<foreach ... item="yearItem" separator=",">
运行 HTML
最终修正版本
xml
复制
<choose><!-- 单值优先 --><when test="budgetYear != null and budgetYear != ''">AND budget_year = #{budgetYear}</when><!-- 多值备选 --><when test="budgetYears != null and budgetYears.length > 0">AND budget_year IN<foreach collection="budgetYears" item="yearItem" open="(" close=")" separator=",">#{yearItem, jdbcType=VARCHAR, javaType=string}</foreach></when> </choose>
运行 HTML
其他注意事项
-
SQL 注入风险:
虽然 MyBatis 预处理语句能防御大部分注入,但确保budgetYear
不直接拼接 SQL -
性能影响:
IN
子句随着数组变大可能影响性能(建议超过 1000 值时改用临时表) -
兼容性:
如果budgetYears
可能是 Collection 而非数组,需要改用!isEmpty()
判断