使用JDBC为参数化IN子句的最佳方法是什么?
使用JDBC为参数化IN子句的最佳方法是什么?
这个问题已经有了答案:
假设我有一条形式如下的查询
SELECT * FROM MYTABLE WHERE MYCOL in (?)
而我想使in中的参数成为参数。
在Java中,是否有一种简单的方法来使用JDBC实现这一点,以一种不需要修改SQL本身就可以在多个数据库上工作的方式?
我找到的最接近的问题涉及C#,我想知道Java/JDBC是否有不同的方法。
admin 更改状态以发布 2023年5月24日
由于没有人回答针对大型IN语句(超过100)的案例,我将提供我对此问题的解决方案,该方法适用于JDBC。简而言之,我使用临时表将IN
替换为INNER JOIN
。
我做的是创建一个批量id表,在取决于RDBMS的情况下,我可能会将其制作为临时表或内存表。
该表有两列。一列带有来自IN语句的id,另一列带有我实时生成的批处理id。
SELECT * FROM MYTABLE M INNER JOIN IDTABLE T ON T.MYCOL = M.MYCOL WHERE T.BATCH = ?
在进行选择之前,您将自己的id压入具有给定批次id的表中。然后,您只需使用INNER JOIN替换您原始查询中的IN语句,并与您的id表WHERE批次id匹配即可。完成后,您删除您的批次条目。
在JDBC中确实没有直接的方法来执行这个操作。一些JDBC驱动似乎支持在IN
子句上使用PreparedStatement#setArray()
方法。我不确定哪些驱动支持。
你可以使用一个辅助方法,使用String#join()
和Collections#nCopies()
来生成IN
子句的占位符,另外一个辅助方法使用PreparedStatement#setObject()
在循环中设置所有的值。
public static String preparePlaceHolders(int length) { return String.join(",", Collections.nCopies(length, "?")); } public static void setValues(PreparedStatement preparedStatement, Object... values) throws SQLException { for (int i = 0; i < values.length; i++) { preparedStatement.setObject(i + 1, values[i]); } }
这是你可以使用它的方式:
private static final String SQL_FIND = "SELECT id, name, value FROM entity WHERE id IN (%s)"; public Listfind(Set ids) throws SQLException { List entities = new ArrayList (); String sql = String.format(SQL_FIND, preparePlaceHolders(ids.size())); try ( Connection connection = dataSource.getConnection(); PreparedStatement statement = connection.prepareStatement(sql); ) { setValues(statement, ids.toArray()); try (ResultSet resultSet = statement.executeQuery()) { while (resultSet.next()) { entities.add(map(resultSet)); } } } return entities; } private static Entity map(ResultSet resultSet) throws SQLException { Enitity entity = new Entity(); entity.setId(resultSet.getLong("id")); entity.setName(resultSet.getString("name")); entity.setValue(resultSet.getInt("value")); return entity; }
注意,一些数据库有限制IN
子句中允许值的数量。例如,Oracle限制为1000个。