Diesel框架下实现通用任意查询删除函数的疑问:当前复杂实现是否为最简方式?
嘿,我完全懂你这种感受——本来想封装个通用函数简化重复的数据库删除代码,结果被编译器逼着装了一堆看起来眼花缭乱的Trait约束,确实有点头大!先给你吃个定心丸:你的初始实现是正确的,但确实有更简洁的写法,不用手动罗列这么多底层关联类型的约束。
我们先来拆解下为什么会有这么多约束:Diesel的类型系统设计得非常严格,需要确保传入的查询能正确转换成PostgreSQL的删除语句,所以编译器会一步步提示你补全所有必要的Trait要求。不过Diesel本身已经封装了很多高层Trait,我们可以直接利用这些抽象来简化代码。
简化方案一:利用ExecuteDslTrait封装执行约束
ExecuteDsl是Diesel提供的核心Trait之一,它定义了一个查询能够在特定数据库连接上执行的所有必要条件,包括你手动写的QueryFragment、QueryId这些底层约束。我们可以直接用它来替换一堆繁琐的关联类型约束:
use diesel::ExecuteDsl; use diesel::pg::PgConnection; use diesel::result::Error; // 这里的Pool是你定义的diesel::r2d2::Pool<ConnectionManager<PgConnection>>别名 fn delete_direct<T>(pool: &Pool, q: T) -> Result<usize, Error> where T: diesel::query_builder::IntoUpdateTarget, diesel::delete<T>: ExecuteDsl<PgConnection, Output = usize>, { let mut conn = pool.get()?; diesel::delete(q).execute(&mut conn) }
这样调整后,约束条件一下子清爽了很多!而且我们把函数改成返回Result,让调用方可以自己处理错误(当然如果你还是想保留expect的写法,也可以在函数内部处理,不过返回Result更符合Rust的错误处理习惯)。
调用这个函数的方式和你原来的需求完全一致:
use crate::schema::users::dsl::*; delete_direct(&pool, users.filter(name.eq("Harry"))) .expect("Failed to run the delete query you specified");
简化方案二:结合DeleteDsl和RunQueryDsl
如果你更习惯用Diesel的查询DSL风格,也可以用DeleteDsl和RunQueryDsl的组合来简化约束:
use diesel::pg::Pg; use diesel::query_dsl::DeleteDsl; use diesel::RunQueryDsl; use diesel::pg::PgConnection; use diesel::result::Error; fn delete_direct<T>(pool: &Pool, q: T) -> Result<usize, Error> where T: DeleteDsl, <T as DeleteDsl>::Query: RunQueryDsl<PgConnection> + diesel::query_builder::QueryFragment<Pg> + 'static, { let mut conn = pool.get()?; diesel::delete(q).execute(&mut conn) }
这个方案的约束也比你最初的写法简洁不少,本质上是利用Diesel的DSL抽象来减少手动约束的数量。
为什么你的初始实现看起来复杂?
你最初的写法是完全正确的,只是你手动把Diesel底层需要的所有关联类型约束都列出来了——这些约束其实已经被ExecuteDsl或者RunQueryDsl这样的高层Trait封装好了,所以我们没必要手动去写它们。
总结一下:你并没有错过什么“一眼就能看到的魔法”,只是可以通过Diesel提供的高层抽象来简化约束,让代码更干净易读。上面的两种简化方案都能达到你想要的效果,而且比初始实现简洁很多。
备注:内容来源于stack exchange,提问作者mmdts

