Выборка строки перед выполнением обновления / удаления - PullRequest
1 голос
/ 10 февраля 2020

Можно ли извлечь удаленные или обновленные строки до того, как были внесены изменения?

Допустим, у меня есть следующий оператор

        dslContext.deleteFrom(Tables.USERS)
                .where(Tables.USERS.NAME.eq("xy"))
                .execute();

Я хочу напечатать все строки, которые соответствуют where заявление. Я знаю, что есть класс DefaultExecuteListener, который я могу использовать, но я не знаю, как это сделать.

1 Ответ

1 голос
/ 11 февраля 2020

К сожалению, MySQL пока не поддерживает операторы в стиле DELETE .. RETURNING (см. Ниже).

Решение, которое работает на всех СУБД с несколькими циклами обработки

Полное решение, которое будет Работа для всех видов сложных операторов DELETE и UPDATE будет использовать VisitListener для преобразования операторов в эквивалентные операторы SELECT. Я могу вспомнить тонну крайних случаев, которые необходимо учитывать, если вы хотите, чтобы это всегда работало.

Если решение "80/20" достаточно хорошо, тогда Вы можете использовать это простое регулярное выражение ExecuteListener. Вот пример:

create table t (i int primary key, j int);
insert into t values (1, 1), (2, 2), (3, 3);

А затем:

try (Connection c = getConnection()) {
    DSLContext ctx = DSL.using(c);

    ctx.configuration().set(new DefaultExecuteListener() {
        @Override
        public void executeStart(ExecuteContext c) {
            if (c.query() instanceof Delete)
                System.out.println(ctx.fetch(c.sql().replace("delete from", "select * from")));
        }

    });
    System.out.println(ctx.delete(table("t")).where("i > 1").execute());
}

Вывод вышеуказанной программы:

+----+----+
|   i|   j|
+----+----+
|   2|   2|
|   3|   3|
+----+----+
2

Этот подход имеет недостатки, которые могут или могут не имеет значения для вас:

  • Не работает с простыми SQL запросами. Если это проблема, замените чек instanceof Delete более дорогим чеком c.sql().startsWith("delete").
  • Он не учитывает чувствительность к регистру (например, простые операторы SQL DELETE) или отформатирован SQL , Конечно, это поправимо.
  • Он не работает с некоторыми MySQL спецификациями c DELETE, такими как PARTITION или IGNORE, которые вы также можете исправить.
  • Подход предполагает, что можно выполнить второй оператор на том же Connection. Это может быть не так в зависимости от вашей DataSource / модели транзакции.

Решение, которое работает для некоторых не MySQL СУБД

Для полноты информации и будущих читателей В этом ответе я также предложу решение, которое будет работать на DB2, Firebird, Oracle, PostgreSQL и SQL Server, которые имеют форму DELETE .. RETURNING или эквивалентный оператор. В этих СУБД вы можете написать:

dslContext.deleteFrom(Tables.USERS)
          .where(Tables.USERS.NAME.eq("xy"))
          .returning()
          .fetch();

Это удалит записи и вернет все затронутые записи в одном операторе, вместо создания двух циклов.

...