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

考虑下面的SQL语句:

SELECT my_column FROM my_table where search_column IN (?)

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

有什么可行的解决办法?


当前回答

只是为了完整性:只要值集不是太大,您也可以简单地用字符串构造语句,如

... WHERE tab.col = ? OR tab.col = ? OR tab.col = ?

然后你可以把它传递给prepare(),然后在循环中使用setXXX()来设置所有的值。这看起来很讨厌,但许多“大型”商业系统通常都会这样做,直到达到特定于db的限制,例如Oracle中的语句为32 KB(我认为是)。

当然,您需要确保集合永远不会不合理地大,或者在这种情况下进行错误捕获。

其他回答

没有简单的办法。 如果目标是保持较高的语句缓存比(即不是每个参数都创建一条语句),您可以执行以下操作:

创建带有几个参数(例如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++;
}

这适用于我(伪docode):

public class SqlHelper
{
    public static final ArrayList<String>platformList = new ArrayList<>(Arrays.asList("iOS","Android","Windows","Mac"));

    public static final String testQuery = "select * from devices where platform_nm in (:PLATFORM_NAME)";
}

指定绑定:

public class Test extends NamedParameterJdbcDaoSupport
public List<SampleModelClass> runQuery()
{
    //define rowMapper to insert in object of SampleClass
    final Map<String,Object> map = new HashMap<>();
    map.put("PLATFORM_LIST",DeviceDataSyncQueryConstants.platformList);
    return getNamedParameterJdbcTemplate().query(SqlHelper.testQuery, map, rowMapper)
}

对于PreparedStatement中的IN子句,我们可以使用不同的替代方法。

Using Single Queries - slowest performance and resource intensive Using StoredProcedure - Fastest but database specific Creating dynamic query for PreparedStatement - Good Performance but doesn't get benefit of caching and PreparedStatement is recompiled every time. Use NULL in PreparedStatement queries - Optimal performance, works great when you know the limit of IN clause arguments. If there is no limit, then you can execute queries in batch. Sample code snippet is; int i = 1; for(; i <=ids.length; i++){ ps.setInt(i, ids[i-1]); } //set null for remaining ones for(; i<=PARAM_SIZE;i++){ ps.setNull(i, java.sql.Types.INTEGER); }

你可以在这里查看更多关于这些替代方法的细节。

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

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

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

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

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

但它也有一些缺点:

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