预准备的IN条款替代方案?

将 SQL 子句与 实例一起使用的最佳解决方法是什么,由于 SQL 注入攻击安全问题,多个值不支持该子句:一个占位符表示一个值,而不是值列表。INjava.sql.PreparedStatement?

请考虑以下 SQL 语句:

SELECT my_column FROM my_table where search_column IN (?)

使用本质上是一种非工作尝试,首先解决使用原因的解决方法。preparedStatement.setString( 1, "'A', 'B', 'C'" );?

有哪些可用的解决方法?


答案 1

对各种可用选项的分析,以及每个选项的优缺点,可以在Jaanne Boyarsky的JavaRanch Journal上的JDBC条目中的批处理选择语句中找到。

建议的选项包括:

  • 准备 ,为每个值执行它,并在客户端联合结果。只需要一个预准备语句。缓慢而痛苦。SELECT my_column FROM my_table WHERE search_column = ?
  • 准备并执行它。每个 IN 列表的大小需要一个预准备语句。快速而明显。SELECT my_column FROM my_table WHERE search_column IN (?,?,?)
  • 准备并执行它。[或代替这些分号使用。每个 IN 列表的大小需要一个预准备语句。愚蠢的慢,严格地比,所以我不知道为什么博主甚至建议它。SELECT my_column FROM my_table WHERE search_column = ? ; SELECT my_column FROM my_table WHERE search_column = ? ; ...UNION ALLWHERE search_column IN (?,?,?)
  • 使用存储过程构造结果集。
  • 准备N个不同大小的IN列表查询;例如,使用 2、10 和 50 个值。要搜索具有 6 个不同值的 IN 列表,请填充 size-10 查询,使其看起来像 。任何像样的服务器都会在运行查询之前优化重复值。SELECT my_column FROM my_table WHERE search_column IN (1,2,3,4,5,6,6,6,6,6)

这些选项都不是理想的。

如果您使用的是 JDBC4 和支持 的服务器,则最佳选择是按照 Boris 的 anwser 中所述使用。x = ANY(y)PreparedStatement.setArray

不过,似乎没有任何方法可以使用IN列表。setArray


有时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 规范传递数组的某些数据库,此方法可以促进将 slow 转换为 faster 子句条件,然后可以通过调用该方法对其进行扩展。= ?IN (?)any


答案 2

PostgreSQL的解决方案:

final PreparedStatement statement = connection.prepareStatement(
        "SELECT my_column FROM my_table where search_column = ANY (?)"
);
final String[] values = getValues();
statement.setArray(1, connection.createArrayOf("text", values));

try (ResultSet rs = statement.executeQuery()) {
    while(rs.next()) {
        // do some...
    }
}

final PreparedStatement statement = connection.prepareStatement(
        "SELECT my_column FROM my_table " + 
        "where search_column IN (SELECT * FROM unnest(?))"
);
final String[] values = getValues();
statement.setArray(1, connection.createArrayOf("text", values));

try (ResultSet rs = statement.executeQuery()) {
    while(rs.next()) {
        // do some...
    }
}