执行前获取一行 update/delete

Fetching a row before execute update/delete

是否可以在进行更改之前获取已删除或更新的行?

假设我有以下陈述

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

我想打印与 where 语句匹配的所有行。我知道我可以使用 DefaultExecuteListener class,但我不知道该怎么做。

遗憾的是,MySQL 尚不支持 DELETE .. RETURNING 样式语句(见下文)。

适用于所有具有多次往返的 RDBMS 的解决方案

适用于各种复杂的 DELETEUPDATE 语句的彻底解决方案将使用 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 特定的 DELETE 子句,例如 PARTITIONIGNORE,您也可以修复它们。
  • 该方法假设可以在同一个 Connection 上 运行 第二个语句。根据您的 DataSource / 交易模型,情况可能并非如此。

适用于某些非MySQL RDBMS

的解决方案

为了完整性和此答案的未来读者,我还将提供一个适用于 DB2、Firebird、Oracle、PostgreSQL 和 SQL 服务器的解决方案,它们都具有DELETE .. RETURNING 的一种形式或等效语句。在那些 RDBMS 上,你可以写:

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

这将在一条语句中删除记录和 return 所有受影响的记录,而不是创建两次往返。