使用PreparedStatement绑定IN参数报ORA-01722无效数字错误如何解决
报错根因
- 你当前的传参逻辑触发了Oracle的类型转换错误:
AGRID是数值类型字段,你传入的是用逗号拼接的整串字符串(例如"1,2,3"),Oracle尝试将整个带逗号的字符串转为数值时失败,抛出ORA-01722: invalid number错误。 - 本质是对PreparedStatement占位符的逻辑理解错误:单个占位符
?只能对应单个参数值,无法直接匹配IN子句需要的多个值列表,你把拼接好的整个值列表当成单个参数传入,属于错误的传参方式。
正确实现方式
方式一:动态生成对应数量的占位符(通用方案,兼容所有数据库)
这是最常用的无SQL注入风险的实现方案:
- 先调整SQL模板,把IN子句的占位符部分改为可替换的模板标记:
<entry key="GET DATA"> select ATYPE, ANAME, AGRID, AVALUE from ALG_RSV.TERMS where AGRID in (%s) order by 2,1 </entry>
- 代码中根据参数列表的长度动态生成对应数量的占位符,再循环给每个占位符赋值:
List<String> agreeIDs = getAgree(); // 生成和参数数量匹配的占位符串,例如3个参数就生成 ?,?,? String placeholders = String.join(",", Collections.nCopies(agreeIDs.size(), "?")); // 拼装最终SQL String sqlTemplate = CSRConsole.getProperty("GET DATA"); String sqlQuery = String.format(sqlTemplate, placeholders); prepStat = Conn.prepareStatement(sqlQuery); // 循环给每个占位符赋值,注意占位符下标从1开始 for (int i = 0; i < agreeIDs.size(); i++) { // 如果AGRID是数值类型,这里改成prepStat.setInt(i+1, Integer.parseInt(agreeIDs.get(i))) prepStat.setString(i+1, agreeIDs.get(i)); } rs = prepStat.executeQuery();
方式二:使用Oracle原生字符串拆分函数(固定SQL结构方案)
如果不想动态拼接SQL结构,可以使用Oracle的XMLTABLE函数拆分传入的逗号分隔字符串为行,再做匹配:
- 调整SQL模板:
<entry key="GET DATA"> select ATYPE, ANAME, AGRID, AVALUE from ALG_RSV.TERMS where AGRID in ( select to_number(column_value) from xmltable(:agr_ids) ) order by 2,1 </entry>
- 代码直接传入拼接好的字符串即可:
String agrID = String.join(",", agreeIDs); prepStat = Conn.prepareStatement(sqlQuery); prepStat.setString(1, agrID); rs = prepStat.executeQuery();
注意事项
Oracle的IN子句默认最多支持1000个参数,如果你的agreeIDs长度超过1000,需要把列表拆分为多批查询,或者改用临时表关联、子查询等方式实现。
内容的提问来源于stack exchange,提问作者Charu
相关产品推荐
相关产品推荐

