在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 + ");";

其他回答

SetArray是最好的解决方案,但它不适用于许多老司机。下面的解决方法可以在java8中使用

String baseQuery ="SELECT my_column FROM my_table where search_column IN (%s)"

String markersString = inputArray.stream().map(e -> "?").collect(joining(","));
String sqlQuery = String.format(baseSQL, markersString);

//Now create Prepared Statement and use loop to Set entries
int index=1;

for (String input : inputArray) {
     preparedStatement.setString(index++, input);
}

这个解决方案比其他难看的while循环解决方案好,其中查询字符串是通过手动迭代构建的

好吧,所以我不记得我之前是如何(或在哪里)这样做的,所以我来堆栈溢出来快速找到答案。我很惊讶我不能。

所以,很久以前我是用这样的语句来解决IN问题的:

where myColumn in (select regexp_substr(:myList,'[^,]+', 1, level) from dual connect by regexp_substr(:myList,'[^,]+', 1, level) not null)

将myList参数设置为逗号分隔的字符串:a,B,C,D…

注意:该参数必须设置两次!

您可以使用集合。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
     */
}

我遇到了一些与准备好的语句相关的限制:

The prepared statements are cached only inside the same session (Postgres), so it will really work only with connection pooling A lot of different prepared statements as proposed by @BalusC may cause the cache to overfill and previously cached statements will be dropped The query has to be optimized and use indices. Sounds obvious, however e.g. the ANY(ARRAY...) statement proposed by @Boris in one of the top answers cannot use indices and query will be slow despite caching The prepared statement caches the query plan as well and the actual values of any parameters specified in the statement are unavailable.

在建议的解决方案中,我会选择一个不会降低查询性能并且查询次数较少的解决方案。这将是#4(批处理少数查询)从@Don链接或指定NULL值为不需要的'?@Vladimir Dyuzhev提出的标记

这不是理想的做法,但它很简单,大多数时候对我来说都很有效。

where ? like concat( "%|", TABLE_ID , "|%" ) 

然后你穿过?|1|,|2|,|3|,…|