1

我有一些代码可以生成一组我想从数据库表中删除的主键值。

long[] keysToDelete = { 0, 1, 2, 3 };

我想使用 PreparedStatement 来执行相当于

DELETE FROM MyTable WHERE myPrimaryKey IN (0, 1, 2, 3);

知道怎么做吗?

4

3 回答 3

5

两步:

  1. 使用适当的参数 # 构建 PreparedStatement SQL 字符串。
  2. 循环遍历值数组并将每个值绑定到其参数。

不幸的是,没有很好的方法来一次绑定一个数组。

于 2009-01-15T19:12:31.617 回答
1

我编写了一个类来动态生成这样的多参数查询。它目前有一些限制(为了快速编写)并且尚未经过彻底测试,但可能是让您入门的好方法。限制:

  • 只处理一个多参数参数 (??)
  • 错误地将引号中的问号识别为参数
  • API 并不漂亮,但另一种选择是编写一个具有大量状态管理的完整 PreparedStatement 装饰器,这比我愿意投入的工作要多。

来源:

/**
 * A PreparedStatement decorator that can bind a set of arguments
 *
 * A specialized ?? placeholder in a string can be bound to a set of
 * values instead of just single values. Currently, only one such
 * specialized placeholder is supported, and you must bind it before
 * obtaining the prepared statement.
 *
 * If you want to bind additional values to the PreparedStatement after
 * producing it, you must run the parameter index through the param()
 * method.
 *
 * Example use:
 *
 * 
 *     MultiValueBinder binder = new MultiValueBinder(
 *          "UPDATE table SET value = ? WHERE id IN (??)", conn);
 *     binder.setInts(myIds);
 *
 *     PreparedStatement stmt = binder.statement();
 *     stmt.setString(binder.param(1), "myValue");
 *
 *     ResultSet rs = stmt.executeQuery();
 *
 * Note: this class is not robust against using question marks in literal
 * strings. Don't use them :).
 */
public class MultiValueBinder {
    private Connection connection;
    private PreparedStatement statement;
    private String sql;

    private int argumentsBefore = 0;
    private int setSize         = 0;

    public MultiValueBinder(String sql, Connection connection) {
        this.sql        = sql;
        this.connection = connection;
    }

    /**
     * Bind a collection of integers to the multi-valued argument
     */
    public void setInts(Collection<Integer> ints) throws SQLException {
        explodeQuery(ints.size());
        buildStatement();
        try {

            int i = 0;
            for (Integer integer: ints)
                statement.setInt(1 + argumentsBefore + i++, integer);

        } catch (Exception ex) {
            cleanStatement();
            throw (ex instanceof SQLException) ? (SQLException) ex : new SQLException(ex);
        }
    }

    /**
     * Bind a collection of strings to the multi-valued argument
     */
    public void setStrings(Collection<String> strings) throws SQLException {
        explodeQuery(strings.size());
        buildStatement();
        try {

            int i = 0;
            for (String str: strings)
                statement.setString(1 + argumentsBefore + i++, str);

        } catch (Exception ex) {
            cleanStatement();
            throw (ex instanceof SQLException) ? (SQLException) ex : new SQLException(ex);
        }
    }

    /**
     * Explode the multi-value parameter into a sequence of comma-separated
     * question marks.
     */
    private void explodeQuery(int size) throws SQLException {
        int mix = sql.indexOf("??");
        if (mix == -1) throw new SQLException("Query does not contain a multi-valued argument.");
        if (size == 0) throw new SQLException("Can't bind an empty collection; generated SQL won't parse.");

        // Count the number of arguments before the multi-marker
        argumentsBefore = 0;
        for (int i = 0; i < mix; i++) {
            if (sql.charAt(i) == '?') argumentsBefore++;
        }
        setSize = size;

        // Generate the exploded SQL query
        StringBuilder sb = new StringBuilder(sql.substring(0, mix)); // Start
        for (int i = 0; i < setSize; i++) {                          // ?, ?, ...
            if (i > 0) sb.append(", ");
            sb.append('?');
        }
        sb.append(sql.substring(mix + 2));                           // Remainder
        sql = sb.toString();
    }

    /**
     * Create the statement if it hasn't been created yet
     */
    private void buildStatement() throws SQLException {
        if (statement != null) return;
        if (sql.contains("??"))
            throw new SQLException("Multi-valued argument not bound yet.");
        statement = connection.prepareStatement(sql);
    }

    private void cleanStatement() {
        if (statement != null) {
            try {
                statement.close();
            } catch (Exception ex) {
                /* Ignore */
            }
            statement = null;
        }
    }

    public PreparedStatement statement() throws SQLException {
        buildStatement();
        return statement;
    }

    /**
     * Transform the 1-based-index of the given argument before query expansion
     * into the index after expansion.
     *
     * The ?? placeholder takes up one index slot.
     */
    public int param(int ix) {
        if (ix <= argumentsBefore) return ix;
        if (ix == argumentsBefore + 1)
            throw new RuntimeException(ix + " is the index of the multi-valued parameter.");
        return argumentsBefore + 1 + setSize;
    }
}
于 2011-01-13T13:01:53.747 回答
0

不完全确定,但这可能会有所帮助:

PreparedStatement pstmt = Connection.prepareStatement("DELETE FROM MyTable WHERE myPrimaryKey IN (?)");
pstmt.setArray(1, idArray);
于 2009-01-15T19:28:41.800 回答