使用JDBC为参数化IN子句的最佳方法是什么?

27 浏览
0 Comments

使用JDBC为参数化IN子句的最佳方法是什么?

这个问题已经有了答案

PreparedStatement IN子句的替代方法?

假设我有一条形式如下的查询

SELECT * FROM MYTABLE WHERE MYCOL in (?)

而我想使in中的参数成为参数。

在Java中,是否有一种简单的方法来使用JDBC实现这一点,以一种不需要修改SQL本身就可以在多个数据库上工作的方式?

我找到的最接近的问题涉及C#,我想知道Java/JDBC是否有不同的方法。

admin 更改状态以发布 2023年5月24日
0
0 Comments

由于没有人回答针对大型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匹配即可。完成后,您删除您的批次条目。

0
0 Comments

在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 List find(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个。

0