小编典典

PreparedStatement IN 子句的替代方案?

all

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

考虑以下 SQL 语句:

SELECT my_column FROM my_table where search_column IN (?)

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

有哪些解决方法?


阅读 101

收藏
2022-03-28

共1个答案

小编典典

此处提供了对各种可用选项的分析,以及每种选项的优缺点。

建议的选项是:

  • Prepare SELECT my_column FROM my_table WHERE search_column = ?,为每个值执行它并在客户端合并结果。只需要一个准备好的语句。缓慢而痛苦。
  • 准备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 ALL这些分号代替。--ed] 每个大小的 IN 列表需要一个准备好的语句。非常慢,严格来说比 差WHERE search_column IN (?,?,?),所以我不知道为什么博主甚至建议它。
  • 使用存储过程来构造结果集。
  • 准备 N 个不同的 size-of-IN-list 查询;比如说,有 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
和支持的服务器,最好的选择是按照此处所述x =ANY(y)使用PreparedStatement.setArray

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


有时 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子句条件= ?转换为 fastIN (?)子句条件,然后可以通过调用该any方法进行扩展。

2022-03-28