在java.sql实例中使用SQL IN子句的最佳变通方法是什么?由于SQL注入攻击安全问题,不支持多值的PreparedStatement:一个?占位符表示一个值,而不是一个值列表。
考虑下面的SQL语句:
SELECT my_column FROM my_table where search_column IN (?)
使用preparedStatement。setString(1, "'A', 'B', 'C'");本质上是一种无用的尝试,试图解决使用原因?首先。
有什么可行的解决办法?
尝试使用instr函数?
select my_column from my_table where instr(?, ','||search_column||',') > 0
then
ps.setString(1, ",A,B,C,");
不可否认,这是一个肮脏的黑客,但它确实减少了sql注入的机会。在甲骨文工作。
在PreparedStatement中生成查询字符串,使其具有与列表中项目数量相匹配的多个?。这里有一个例子:
public void myQuery(List<String> items, int other) {
...
String q4in = generateQsForIn(items.size());
String sql = "select * from stuff where foo in ( " + q4in + " ) and bar = ?";
PreparedStatement ps = connection.prepareStatement(sql);
int i = 1;
for (String item : items) {
ps.setString(i++, item);
}
ps.setInt(i++, other);
ResultSet rs = ps.executeQuery();
...
}
private String generateQsForIn(int numQs) {
String items = "";
for (int i = 0; i < numQs; i++) {
if (i != 0) items += ", ";
items += "?";
}
return items;
}
对于PreparedStatement中的IN子句,我们可以使用不同的替代方法。
Using Single Queries - slowest performance and resource intensive
Using StoredProcedure - Fastest but database specific
Creating dynamic query for PreparedStatement - Good Performance but doesn't get benefit of caching and PreparedStatement is recompiled every time.
Use NULL in PreparedStatement queries - Optimal performance, works great when you know the limit of IN clause arguments. If there is no limit, then you can execute queries in batch.
Sample code snippet is;
int i = 1;
for(; i <=ids.length; i++){
ps.setInt(i, ids[i-1]);
}
//set null for remaining ones
for(; i<=PARAM_SIZE;i++){
ps.setNull(i, java.sql.Types.INTEGER);
}
你可以在这里查看更多关于这些替代方法的细节。
只是为了完整起见,因为我没有看到其他人提出这个建议:
在实现上述任何复杂的建议之前,请考虑SQL注入是否确实是您的场景中的一个问题。
在许多情况下,提供给In(…)的值是一个id列表,这些id以某种方式生成,您可以确保不可能进行注入…(例如,之前select some_id from some_table where some_condition.)
如果是这种情况,您可能只是连接这个值,而不为它使用服务或准备好的语句,或将它们用于此查询的其他参数。
query="select f1,f2 from t1 where f3=? and f2 in (" + sListOfIds + ");";
在研究了不同论坛上的各种解决方案后,没有找到一个好的解决方案,我觉得下面的方法是最容易遵循和编码的:
示例:假设您有多个参数要传递到' 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”,以确保查询中不可能出现这样的单词。希望你得到了答案。
注意:虽然这不是一个准备好的查询,但它完成了我希望代码完成的工作。