不幸的是,MySQL 还不支持DELETE .. RETURNING 样式语句(见下文)。
适用于所有具有多个往返行程的 RDBMS 的解决方案
适用于各种复杂的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 特定的
DELETE 子句,例如 PARTITION 或 IGNORE,您也可以修复它们。
- 该方法假定可以在同一个
Connection 上运行第二条语句。根据您的DataSource/事务模型,情况可能并非如此。
适用于某些非 MySQL RDBMS 的解决方案
为了完整起见和此答案的未来读者,我还将提供一个适用于 DB2、Firebird、Oracle、PostgreSQL 和 SQL Server 的解决方案,它们都具有DELETE .. RETURNING 或等效语句的形式。在这些 RDBMS 上,您可以编写:
dslContext.deleteFrom(Tables.USERS)
.where(Tables.USERS.NAME.eq("xy"))
.returning()
.fetch();
这将删除记录并在一个语句中返回所有受影响的记录,而不是创建两个往返。