假设我有一个表格查询
SELECT * FROM MYTABLE WHERE MYCOL in (?)
我想将参数参数化为 in。
有没有一种直接的方法可以在 Java 中使用 JDBC 执行此操作,并且可以在不修改 SQL 本身的情况下在多个数据库上工作?
我发现的最接近的问题与 C# 有关,我想知道 Java/JDBC 是否有不同之处。
在 JDBC 中确实没有直接的方法可以做到这一点。一些JDBC 驱动程序似乎支持PreparedStatement#setArray()
该IN
子句。我只是不确定哪些是。
您可以只使用带有String#join()
and的辅助方法Collections#nCopies()
来生成占位符 forIN
子句,并使用另一个辅助方法来设置循环中的所有值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 个项目有此限制。
由于没有人回答大型 IN 子句(超过 100 个)的情况,我将提出我的解决方案来解决这个问题,它非常适用于 JDBC。简而言之,我用tmp 表上的 aIN
替换。INNER JOIN
我所做的是制作我所说的批处理 ids 表,根据 RDBMS,我可以制作一个 tmp 表或内存表。
该表有两列。一列具有来自 IN 子句的 id,另一列具有我即时生成的批处理 id。
SELECT * FROM MYTABLE M INNER JOIN IDTABLE T ON T.MYCOL = M.MYCOL WHERE T.BATCH = ?
在您选择之前,将您的 id 推入具有给定批次 id 的表中。然后,您只需将原始查询 IN 子句替换为与您的 ids 表匹配的 INNER JOIN WHERE batch_id 等于您当前的批次。完成后,您可以批量删除条目。
执行此操作的标准方法是(如果您使用 Spring JDBC)是使用org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate类。
使用此类,可以将 List 定义为 SQL 参数,并使用 NamedParameterJdbcTemplate 替换命名参数。例如:
public 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;
}
我通过使用尽可能多的值来构造 SQL 字符串来解决这个问题?
。
SELECT * FROM MYTABLE WHERE MYCOL in (?,?,?,?)
首先,我搜索了可以传递到语句中的数组类型,但所有 JDBC 数组类型都是特定于供应商的。所以我留在了多个?
.
SQL 标准允许基于包含变量值列表的表达式来选择行。一个典型的例子是 select * from T_ACTOR where id in (1, 2, 3)。JDBC 标准不直接支持准备好的语句使用此变量列表;您不能声明可变数量的占位符。您需要准备好所需数量的占位符的多种变体,或者您需要在知道需要多少占位符后动态生成 SQL 字符串。NamedParameterJdbcTemplate 和 JdbcTemplate 中提供的命名参数支持采用后一种方法。将值作为原始对象的 java.util.List 传递。此列表将用于在语句执行期间插入所需的占位符并传入值。
希望这可以帮到你。
AFAIK,JDBC 中没有标准支持将集合作为参数处理。如果你可以传入一个 List 并且它会被扩展,那就太好了。
Spring 的 JDBC 访问支持将集合作为参数传递。你可以看看这是如何完成的,以获得安全编码的灵感。
(本文首先讨论 Hibernate,然后讨论 JDBC。)
我们可以使用不同的替代方法。
在此处查看有关这些的更多详细信息。
看我的试用和它成功,据说列表大小有潜在的限制。列表 l = Arrays.asList(new Integer[]{12496,12497,12498,12499}); 地图参数 = 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());
}
我能想到的一种方法是使用 java.sql.PreparedStatement 和一些陪审团操纵
PreparedStatement PreparedStmt = conn.prepareStatement("SELECT * FROM MYTABLE WHERE MYCOL in (?)");
... 接着 ...
PreparedStmt.setString(1, [你的字符串参数]);
http://java.sun.com/docs/books/tutorial/jdbc/basics/prepared.html