Rust 用 diesel 构建类型安全的数据访问层

test12026-09-274 次阅读

diesel 的定位

sqlx 是运行时校验 SQL,diesel 则把表结构编译进类型系统:字段名、列类型、插入与查询都在编译期检查,拼错列名或类型不匹配会直接编译失败。它生成的 SQL 可控、零额外运行时反射,适合对性能与正确性都有要求的数据访问层。

定义表结构与模型

table! 宏从数据库视角声明表,模型结构用 Queryable 映射查询结果、Insertable 映射写入、Selectable 支撑类型安全的查询构造。

use diesel::prelude::*;

table! {
    users (id) {
        id -> Int4,
        name -> Varchar,
        email -> Varchar,
        created_at -> Timestamptz,
    }
}

#[derive(Queryable, Selectable, Debug)]
#[diesel(table_name = users)]
struct User {
    id: i32,
    name: String,
    email: String,
    created_at: chrono::DateTime<chrono::Utc>,
}

#[derive(Insertable)]
#[diesel(table_name = users)]
struct NewUser<'a> {
    name: &'a str,
    email: &'a str,
}

建立连接与迁移

连接用 PgConnection,生产环境应换成带连接池的 r2d2 或 bb8。迁移文件由 diesel_cli 生成,保证各环境 schema 一致。

use diesel::pg::PgConnection;
use diesel::Connection;

fn connect() -> PgConnection {
    PgConnection::establish("postgres://user:pass@localhost/app")
        .expect("无法连接数据库")
}

查询与写入

插入用 insert_into 配合 NewUser,返回自增主键;查询用 filter 与 load 把结果映射成 User 向量。

use crate::schema::users::dsl::*;

fn create_user(conn: &mut PgConnection, name: &str, email: &str) -> User {
    let new_user = NewUser { name, email };
    diesel::insert_into(users)
        .values(&new_user)
        .get_result(conn)
        .expect("插入用户失败")
}

fn active_users(conn: &mut PgConnection) -> Vec<User> {
    users
        .filter(email.like("%@example.com"))
        .order(created_at.desc())
        .limit(20)
        .select(User::as_select())
        .load(conn)
        .expect("查询失败")
}

关联查询

多表关联用 belongs_to 与 join。下面给 posts 表声明它属于 users,再用 inner_join 一次性取出作者信息。

table! {
    posts (id) {
        id -> Int4,
        user_id -> Int4,
        title -> Varchar,
    }
}

#[derive(Queryable, Selectable)]
#[diesel(table_name = posts)]
struct Post {
    id: i32,
    user_id: i32,
    title: String,
}

fn posts_with_author(conn: &mut PgConnection) -> Vec<(Post, User)> {
    posts
        .inner_join(users)
        .select((Post::as_select(), User::as_select()))
        .load(conn)
        .expect("联表查询失败")
}
  • 表宏与实际库表结构必须一致,靠迁移文件单向保证。
  • 编译期就能发现的 SQL 错误,省去大量联调时间。
  • 生产连接务必走连接池,别每次请求都建立新连接。
T

test1

文章作者

diesel 的定位 sqlx 是运行时校验 SQL,diesel 则把表结构编译进类型系统:字段名、列类型、插入与查...

分类
技术
发布时间
2026-09-27
字数
约 2237 字
阅读
4 次