PreparedStatement With IN Clause

Last Modified: 2022/12/15

预编译语句遇到 IN

PreparedStatement 又称为预编译语句,它在遇到 IN 语句的时候会产生一些违反直觉的地方,考虑下面的语句:

PreparedStatement ps = conn.prepareStatement(
    "select * from users where id in (?)");

你可能希望 ps 有个 setList 或者 setArray 方法直接设置一个 list 或者数组,就像下面这样绑定参数:

List<String> ids = Arrays.asList("1", "2");
ps.setList(1, ids);

然而,这种写法并不能绑定 list 或者数组中的多个参数,因为上面的 sql 语句中只有一个占位符,只能绑定一个值,如果 list 中有三个元素,实际上你需要三个占位符。正确的写法应该是:

List<String> ids = Arrays.asList("1", "2");
String sql = String.format("select * from users where id in (%s)",
     ids.stream().map(v -> "?").collect(Collectors.joining(", ")));
PreparedStatement ps = conn.prepareStatement(sql);    
int index = 1;
for (Object id : ids) {
   ps.setObject(index++, id);
}

Mybatis 中处理 IN

在 Mybatis 中,当你遇到 IN 语句的时候,同样的场景,我们一般这样写:

<select ...>
  select * from users
  WHERE id IN
  <foreach item="id" collection="ids" open="(" separator="," close=")">
      #{id}
  </foreach>
</select>

如果你打开了 Mybatis 的 sql 日志,你就会发现,Mybatis 使用的也是 PreparedStatement,同时生成的 sql 语句为:

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

结语

本文介绍了 PreparedStatement 遇到 IN 产生的问题和解决办法,这种方法可以有效的避免 sql 注入,然而也可能会带来一定的性能问题,有关性能问题的描述,感兴趣的读者可以参考 Batching Select Statements in JDBC

有问题吗?点此反馈!

温馨提示:反馈需要登录