43

假设我有一个表格查询

SELECT * FROM MYTABLE WHERE MYCOL in (?)

我想将参数参数化为 in。

有没有一种直接的方法可以在 Java 中使用 JDBC 执行此操作,并且可以在不修改 SQL 本身的情况下在多个数据库上工作?

我发现的最接近的问题与 C# 有关,我想知道 Java/JDBC 是否有不同之处。

4

10 回答 10

48

在 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 个项目有此限制。

于 2010-05-18T22:09:52.213 回答
15

由于没有人回答大型 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 等于您当前的批次。完成后,您可以批量删除条目。

于 2012-06-20T12:33:06.143 回答
9

执行此操作的标准方法是(如果您使用 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;
}
于 2013-02-18T14:02:29.150 回答
4

我通过使用尽可能多的值来构造 SQL 字符串来解决这个问题?

SELECT * FROM MYTABLE WHERE MYCOL in (?,?,?,?)

首先,我搜索了可以传递到语句中的数组类型,但所有 JDBC 数组类型都是特定于供应商的。所以我留在了多个?.

于 2010-05-18T21:21:59.960 回答
3

我从docs.spring(19.7.3)得到了答案

SQL 标准允许基于包含变量值列表的表达式来选择行。一个典型的例子是 select * from T_ACTOR where id in (1, 2, 3)。JDBC 标准不直接支持准备好的语句使用此变量列表;您不能声明可变数量的占位符。您需要准备好所需数量的占位符的多种变体,或者您需要在知道需要多少占位符后动态生成 SQL 字符串。NamedParameterJdbcTemplate 和 JdbcTemplate 中提供的命名参数支持采用后一种方法。将值作为原始对象的 java.util.List 传递。此列表将用于在语句执行期间插入所需的占位符并传入值。

希望这可以帮到你。

于 2016-11-30T02:30:20.510 回答
1

AFAIK,JDBC 中没有标准支持将集合作为参数处理。如果你可以传入一个 List 并且它会被扩展,那就太好了。

Spring 的 JDBC 访问支持将集合作为参数传递。你可以看看这是如何完成的,以获得安全编码的灵感。

请参阅将集合自动扩展为 JDBC 参数

(本文首先讨论 Hibernate,然后讨论 JDBC。)

于 2010-05-18T21:44:49.087 回答
0

我们可以使用不同的替代方法。

  1. 执行单个查询 - 速度慢且不推荐
  2. 使用存储过程 - 特定于数据库
  3. 动态创建 PreparedStatement 查询 - 性能良好,但缓存的好处松散,需要重新编译
  4. 在 PreparedStatement 查询中使用 NULL - 我认为这是一种具有最佳性能的好方法。

在此处查看有关这些的更多详细信息。

于 2014-01-26T13:41:00.737 回答
0

看我的试用和它成功,据说列表大小有潜在的限制。列表 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);
于 2012-02-08T07:10:20.747 回答
0

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());
}
于 2012-02-16T19:20:00.903 回答
-2

我能想到的一种方法是使用 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

于 2010-05-18T21:34:55.433 回答