假设我有一个查询形式如下:
SELECT * FROM MYTABLE WHERE MYCOL in (?)
我希望将参数化为" in "的参数。
在Java中,使用JDBC是否有一种简单的方法可以实现这一点,而不需要修改SQL本身,可以适用于多个数据库?
最接近的问题是关于C#的, 我想知道Java/JDBC是否有不同的解决方案。
在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<Entity> find(Set<Long> ids) throws SQLException {
List<Entity> entities = new ArrayList<Entity>();
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个项目有此限制。
由于没有人回答一个包含超过100个元素的大型IN语句问题,我将提供我的解决方案,对于JDBC来说,这个方法可以很好地解决问题。简而言之,我将用一个临时表上的INNER JOIN
替换IN
。
我所做的是创建一个批处理ID表,根据关系数据库管理系统的不同,我可能会将其设置为临时表或内存表。
该表有两列,一列是IN子句中的ID,另一列是我动态生成的批次ID。
SELECT * FROM MYTABLE M INNER JOIN IDTABLE T ON T.MYCOL = M.MYCOL WHERE T.BATCH = ?
IN
或EXISTS
谓词)可能会比内连接更高效吧? - Lukas Ederpublic List<MyObject> getDatabaseObjects(List<String> params) {
NamedParameterJdbcTemplate jdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
String sql = "select * from my_table where my_col in (:params)";
List<MyObject> result = jdbcTemplate.query(sql, Collections.singletonMap("params", params), myRowMapper);
return result;
}
?
来解决了这个问题。SELECT * FROM MYTABLE WHERE MYCOL in (?,?,?,?)
首先,我搜索了可以传递到语句中的数组类型,但所有的JDBC数组类型都是特定于供应商的。因此,我仍然使用了多个?
。
看到我的试验成功了,据说列表大小有潜在的限制。 List l = Arrays.asList(new Integer[]{12496,12497,12498,12499}); Map param = Collections.singletonMap("goodsid",l);
NamedParameterJdbcTemplate namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(getJdbcTemplate().getDataSource());
String sql = "SELECT bg.goodsid FROM beiker_goods bg WHERE bg.goodsid in(:goodsid)";
List<Long> list = namedParameterJdbcTemplate.queryForList(sql, param2, Long.class);
sormula让这变得简单(参见示例4):
ArrayList<Integer> partNumbers = new ArrayList<Integer>();
partNumbers.add(999);
partNumbers.add(777);
partNumbers.add(1234);
// set up
Database database = new Database(getConnection());
Table<Inventory> inventoryTable = database.getTable(Inventory.class);
// select operation for list "...WHERE PARTNUMBER IN (?, ?, ?)..."
for (Inventory inventory: inventoryTable.
selectAllWhere("partNumberIn", partNumbers))
{
System.out.println(inventory.getPartNumber());
}
我们可以使用不同的替代方法。
有关这些的更多详细信息在此处查看。
http://java.sun.com/docs/books/tutorial/jdbc/basics/prepared.html
... WHERE MYCOL IN ('2,3,5,6')
,这不是你想要的查询。 - Progman