在java.sql实例中使用SQL IN子句的最佳变通方法是什么?由于SQL注入攻击安全问题,不支持多值的PreparedStatement:一个?占位符表示一个值,而不是一个值列表。

考虑下面的SQL语句:

SELECT my_column FROM my_table where search_column IN (?)

使用preparedStatement。setString(1, "'A', 'B', 'C'");本质上是一种无用的尝试,试图解决使用原因?首先。

有什么可行的解决办法?


当前回答

对于某些情况,regexp可能会有所帮助。 下面是我在Oracle上查看的一个例子,它是有效的。

select * from my_table where REGEXP_LIKE (search_column, 'value1|value2')

但它也有一些缺点:

它应用的任何列都应该转换为varchar/char,至少是隐式转换。 使用特殊字符时要小心。 它会降低性能——在我的例子中,in版本使用索引和范围扫描,而REGEXP版本执行全扫描。

其他回答

我认为您可以(使用基本的字符串操作)在PreparedStatement中生成查询字符串,使其具有与列表中项的数量相匹配的若干?

当然,如果你这样做,你只是一个步骤,生成一个巨大的链或在你的查询,但没有正确的数字?在查询字符串中,我不知道你还能如何解决这个问题。

在javaranche Journal上Jeanne Boyarsky的JDBC批处理选择语句条目中,有对各种可用选项及其优缺点的分析。

建议的方案是:

Prepare SELECT my_column FROM my_table WHERE search_column = ?, execute it for each value and UNION the results client-side. Requires only one prepared statement. Slow and painful. Prepare SELECT my_column FROM my_table WHERE search_column IN (?,?,?) and execute it. Requires one prepared statement per size-of-IN-list. Fast and obvious. Prepare SELECT my_column FROM my_table WHERE search_column = ? ; SELECT my_column FROM my_table WHERE search_column = ? ; ... and execute it. [Or use UNION ALL in place of those semicolons. --ed] Requires one prepared statement per size-of-IN-list. Stupidly slow, strictly worse than WHERE search_column IN (?,?,?), so I don't know why the blogger even suggested it. Use a stored procedure to construct the result set. Prepare N different size-of-IN-list queries; say, with 2, 10, and 50 values. To search for an IN-list with 6 different values, populate the size-10 query so that it looks like SELECT my_column FROM my_table WHERE search_column IN (1,2,3,4,5,6,6,6,6,6). Any decent server will optimize out the duplicate values before running the query.

这些选择都不理想。

如果您正在使用JDBC4并且服务器支持x = ANY(y),那么最好的选择是使用PreparedStatement。setArray的描述在鲍里斯的回答。

不过,似乎没有任何方法可以使setArray与IN-lists一起工作。


有时SQL语句是在运行时加载的(例如,从属性文件中加载),但需要可变数量的参数。在这种情况下,首先定义查询:

query=SELECT * FROM table t WHERE t.column IN (?)

接下来,加载查询。然后在运行它之前确定参数的数量。一旦参数计数已知,运行:

sql = any( sql, count );

例如:

/**
 * Converts a SQL statement containing exactly one IN clause to an IN clause
 * using multiple comma-delimited parameters.
 *
 * @param sql The SQL statement string with one IN clause.
 * @param params The number of parameters the SQL statement requires.
 * @return The SQL statement with (?) replaced with multiple parameter
 * placeholders.
 */
public static String any(String sql, final int params) {
    // Create a comma-delimited list based on the number of parameters.
    final StringBuilder sb = new StringBuilder(
        String.join(", ", Collections.nCopies(possibleValue.size(), "?")));

    // For more than 1 parameter, replace the single parameter with
    // multiple parameter placeholders.
    if (sb.length() > 1) {
        sql = sql.replace("(?)", "(" + sb + ")");
    }

    // Return the modified comma-delimited list of parameters.
    return sql;
}

对于某些不支持通过JDBC 4规范传递数组的数据库,此方法可以方便地转换缓慢的= ?转换为更快的IN(?)子句条件,然后可以通过调用any方法进行扩展。

Sormula支持SQL IN操作符,它允许你提供一个java.util.Collection对象作为参数。它使用?对于集合中的每个元素。参见示例4(示例中的SQL是一个注释,用于澄清Sormula创建但不使用的内容)。

而不是使用

SELECT my_column FROM my_table where search_column IN (?)

使用Sql语句作为

select id, name from users where id in (?, ?, ?)

and

preparedStatement.setString( 1, 'A');
preparedStatement.setString( 2,'B');
preparedStatement.setString( 3, 'C');

或者使用存储过程,这将是最好的解决方案,因为sql语句将被编译并存储在数据库服务器中

你可以使用PreparedStatement的setArray()方法

PreparedStatement statement = connection.prepareStatement("Select * from emp where field in (?)");
statement.setArray(1, Arrays.asList(1,2,3,4,5));
ResultSet rs = statement.executeQuery();