Используя JDBC, как я могу заменить несколько идентификаторов на «УДАЛИТЬ ИЗ ГДЕ ИД В (?)» - PullRequest
1 голос
/ 15 января 2009

У меня есть код, который создает набор значений первичного ключа, который я хочу удалить из таблицы базы данных.

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

и я хотел бы использовать PreparedStatement для выполнения эквивалента

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

Есть идеи как?

Ответы [ 3 ]

5 голосов
/ 15 января 2009

Два шага:

  1. Создайте строку SQL PreparedStatement с соответствующим количеством параметров.
  2. Зацикливать массив значений и связывать каждое из них со своим параметром.

К сожалению, нет хорошего способа связать массив сразу.

1 голос
/ 13 января 2011

Я написал класс для динамической генерации такого многопараметрического запроса. В настоящее время он имеет некоторые ограничения (для быстроты написания) и не был тщательно протестирован, но может быть хорошим способом для начала. Ограничения:

  • Обрабатывает только один параметр с несколькими аргументами (??)
  • Ложно распознает вопросительные знаки в кавычках как параметры
  • 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;
    }
}
0 голосов
/ 15 января 2009

Не совсем уверен, но это может помочь:

PreparedStatement pstmt = Connection.prepareStatement("DELETE FROM MyTable WHERE myPrimaryKey IN (?)");
pstmt.setArray(1, idArray);
...