在java.sql实例中使用SQL IN子句的最佳变通方法是什么?由于SQL注入攻击安全问题,不支持多值的PreparedStatement:一个?占位符表示一个值,而不是一个值列表。
考虑下面的SQL语句:
SELECT my_column FROM my_table where search_column IN (?)
使用preparedStatement。setString(1, "'A', 'B', 'C'");本质上是一种无用的尝试,试图解决使用原因?首先。
有什么可行的解决办法?
只是为了完整起见,因为我没有看到其他人提出这个建议:
在实现上述任何复杂的建议之前,请考虑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 + ");";
您可以使用集合。nCopies生成一个占位符集合,并使用String.join将它们连接起来:
List<String> params = getParams();
String placeHolders = String.join(",", Collections.nCopies(params.size(), "?"));
String sql = "select * from your_table where some_column in (" + placeHolders + ")";
try ( Connection connection = getConnection();
PreparedStatement ps = connection.prepareStatement(sql)) {
int i = 1;
for (String param : params) {
ps.setString(i++, param);
}
/*
* Execute query/do stuff
*/
}
没有简单的办法。
如果目标是保持较高的语句缓存比(即不是每个参数都创建一条语句),您可以执行以下操作:
创建带有几个参数(例如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++;
}