nested exception is org.apache.ibatis.binding.BindingException: Parameter 'rangeItem' not found. Available parameters are [md, column, page, param3, param4, param1, param2, order]
错误代码如下:
XML
<if test="md.rangeList != null and md.rangeList.size() > 0">
<foreach collection="md.rangeList" item="rangeItem" separator="OR">
a.meter_number BETWEEN #{rangeItem[0]} AND #{rangeItem[1]}
</foreach>
</if>
第一种改法容易被sql注入谨慎使用,将#{}改为${}
核心区别
一、${} 和 #{} 的核心区别
| 特性 | ${} (字符串替换 / 直接拼接) | #{} (预编译参数占位符) |
|---|---|---|
| 底层实现 | 直接将参数拼接到 SQL 语句中(字符串替换) | 基于 JDBC PreparedStatement 的 ? 占位符 |
| 参数处理 | 不做类型转换,原样拼接(需手动处理引号) | 自动做类型转换,自动添加引号(如字符串) |
| SQL 注入风险 | 高(直接拼接,无过滤) | 低(预编译,参数与 SQL 分离) |
| 适用场景 | 动态表名、列名、排序字段、分表分库等动态语法 | 普通参数(where 条件、插入值、更新值等) |
| 语法限制 | 可拼接任意 SQL 片段,无类型限制 | 仅能替换 "参数值",不能替换 SQL 关键字 / 标识符 |
二、为什么用 #{} 会报错?
#{} 的核心是替换 "参数值",而非 "SQL 语法 / 标识符",如果用在不支持占位符的场景,就会触发报错,常见原因如下:
1. 替换 SQL 标识符(表名 / 列名 / 排序字段)
JDBC 的 PreparedStatement 占位符(?)仅支持替换 "值" ,不支持替换表名、列名、ORDER BY 后的字段等 "标识符"。如果强行用 #{} 替换这些内容,会导致 SQL 语法错误
2. 替换 SQL 关键字(如 ORDER BY 的 ASC/DESC)
同理,排序方向(ASC/DESC)是 SQL 关键字,不能用 #{} 替换
3. 参数类型不匹配(少见,但易忽略)
#{} 会自动做类型转换,如果参数类型与数据库字段类型不兼容,也可能报错:
- 例如:Java 传
Boolean.TRUE,但数据库字段是VARCHAR,#{}会转换为1(MySQL),若字段不支持数字,会报类型错误; - 解决:确保参数类型与数据库字段匹配,或手动转换。
4. 特殊 SQL 语法(如 IN 子句)
如果直接用 #{} 传递数组 / 集合到 IN 子句,会报错(因为 #{} 会将数组解析为单个值)
三、总结:如何选择?
| 场景 | 推荐用法 | 注意事项 |
|---|---|---|
| 普通参数(where 条件、插入值) | #{} | 安全,避免 SQL 注入 |
| 动态表名 / 列名 / 排序字段 / 关键字 | ${} | 1. 仅用于可信参数;2. 手动过滤防注入 |
| IN 子句(数组 / 集合) | <foreach> + #{} |
不要直接用 #{} 或 ${} 拼接数组 |
四. 修改方案
XML
<if test="md.rangeList != null and md.rangeList.size() > 0">
<foreach collection="md.rangeList" item="rangeItem" separator="OR" open="(" close=")">
a.meter_number BETWEEN ${rangeItem[0]} AND ${rangeItem[1]}
</foreach>
</if>
第二种方法就是将列表(List,Set,Map)里面的数组封装为对象去调用
java
package org.jeecg.modules.meter.vo;
import lombok.Data;
@Data
public class MeterRangeVo {
/**
* 开始表身号
*/
private String startMeterNumber;
/**
* 截止表身号
*/
private String endMeterNumber;
public MeterRangeVo(String startNum, String endNum) {
this.startMeterNumber = startNum;
this.endMeterNumber = endNum;
}
}
XML
<if test="md.rangeList != null and md.rangeList.size() > 0">
<foreach collection="md.rangeList" item="rangeItem" separator="OR" open="(" close=")">
a.meter_number BETWEEN #{rangeItem.startMeterNumber} AND #{rangeItem.endMeterNumber}
</foreach>
</if>