在java.sql实例中使用SQL IN子句的最佳变通方法是什么?由于SQL注入攻击安全问题,不支持多值的PreparedStatement:一个?占位符表示一个值,而不是一个值列表。
考虑下面的SQL语句:
SELECT my_column FROM my_table where search_column IN (?)
使用preparedStatement。setString(1, "'A', 'B', 'C'");本质上是一种无用的尝试,试图解决使用原因?首先。
有什么可行的解决办法?
没有简单的办法。
如果目标是保持较高的语句缓存比(即不是每个参数都创建一条语句),您可以执行以下操作:
创建带有几个参数(例如10个)的语句:
... 一个在 (?,?,?,?,?,?,?,?,?,?) ...
绑定所有实际参数
setString(1、“foo”);
setString(2,“酒吧”);
其余的绑定为NULL
Types.VARCHAR setNull (3)
...
Types.VARCHAR setNull (10)
NULL从不匹配任何东西,因此它会被SQL计划构建器优化。
当你将List传递给DAO函数时,逻辑很容易自动化:
while( i < param.size() ) {
ps.setString(i+1,param.get(i));
i++;
}
while( i < MAX_PARAMS ) {
ps.setNull(i+1,Types.VARCHAR);
i++;
}
我的解决方案是:
create or replace type split_tbl as table of varchar(32767);
/
create or replace function split
(
p_list varchar2,
p_del varchar2 := ','
) return split_tbl pipelined
is
l_idx pls_integer;
l_list varchar2(32767) := p_list;
l_value varchar2(32767);
begin
loop
l_idx := instr(l_list,p_del);
if l_idx > 0 then
pipe row(substr(l_list,1,l_idx-1));
l_list := substr(l_list,l_idx+length(p_del));
else
pipe row(l_list);
exit;
end if;
end loop;
return;
end split;
/
现在你可以使用一个变量来获取表中的一些值:
select * from table(split('one,two,three'))
one
two
three
select * from TABLE1 where COL1 in (select * from table(split('value1,value2')))
value1 AAA
value2 BBB
因此,预处理语句可以是:
"select * from TABLE where COL in (select * from table(split(?)))"
问候,
哈维尔·伊瓦内斯
在研究了不同论坛上的各种解决方案后,没有找到一个好的解决方案,我觉得下面的方法是最容易遵循和编码的:
示例:假设您有多个参数要传递到' in '子句中。只要在'IN'子句中放入一个虚拟字符串,例如,“PARAM”确实表示将在这个虚拟字符串中出现的参数列表。
select * from TABLE_A where ATTR IN (PARAM);
您可以在Java代码中将所有参数收集到一个String变量中。可以这样做:
String param1 = "X";
String param2 = "Y";
String param1 = param1.append(",").append(param2);
你可以将所有用逗号分隔的参数附加到一个String变量中,在我们的例子中是'param1'。
在将所有参数收集到单个字符串中之后,您可以将查询中的虚拟文本替换为参数String,即param1,在本例中为“PARAM”。以下是你需要做的:
String query = query.replaceFirst("PARAM",param1); where we have the value of query as
query = "select * from TABLE_A where ATTR IN (PARAM)";
现在可以使用executeQuery()方法执行查询。只要确保在查询中没有“PARAM”这个词。您可以使用特殊字符和字母的组合来代替单词“PARAM”,以确保查询中不可能出现这样的单词。希望你得到了答案。
注意:虽然这不是一个准备好的查询,但它完成了我希望代码完成的工作。
下面是我在自己的应用程序中解决这个问题的方法。理想情况下,您应该使用StringBuilder而不是使用+来表示字符串。
String inParenthesis = "(?";
for(int i = 1;i < myList.size();i++) {
inParenthesis += ", ?";
}
inParenthesis += ")";
try(PreparedStatement statement = SQLite.connection.prepareStatement(
String.format("UPDATE table SET value='WINNER' WHERE startTime=? AND name=? AND traderIdx=? AND someValue IN %s", inParenthesis))) {
int x = 1;
statement.setLong(x++, race.startTime);
statement.setString(x++, race.name);
statement.setInt(x++, traderIdx);
for(String str : race.betFair.winners) {
statement.setString(x++, str);
}
int effected = statement.executeUpdate();
}
如果您决定以后更改查询,使用上面的x这样的变量而不是具体的数字会有很大帮助。