Diesel 是 Rust 生态中的一个强类型、安全、高性能的 ORM(对象关系映射)和查询构建器。它的核心设计理念是 充分利用 Rust 的类型系统,在编译时保证 SQL 查询的正确性,从而减少运行时错误。

安装与编译环境

安装 diesel_cli 并选择数据库后端

在终端执行以下命令,根据你使用的数据库安装对应的 Diesel CLI 版本:

# 安装支持 PostgreSQL 的 Diesel CLI
cargo install diesel_cli --no-default-features --features postgres

# 安装支持 SQLite 的 Diesel CLI
cargo install diesel_cli --no-default-features --features sqlite

# 安装支持 MySQL 的 Diesel CLI
cargo install diesel_cli --no-default-features --features mysql
# 其它略

数据库依赖(以 postgres 为例)

根据不同的数据库,可能需要环境下有不同的依赖,此处以 postgres 为例

Linux (Ubuntu/Debian)

sudo apt update
sudo apt install libpq-dev

macOS

brew install postgresql

windows

若在windows下 postgres 需要 libpq.lib 作为依赖,实际上在安装postgres数据库的时候就会有这个依赖,但是rust在索引依赖上无法定位。

尝试将文件夹添加到 PATH 变量并没有效果,因为它并未被用于传递给 link.exe/LIBPATH 参数。可以尝试该目录:

C:\\Users\\<username>\\.rustup\\toolchains\\stable-x86_64-pc-windows-msvc\\lib\\rustlib\\x86_64-pc-windows-msvc\\lib

libpq.lib 复制到该目录,它就会被正确引用。

工作环境配置

使用环境变量(推荐)

将数据库地址添加到 .env ,并在代码中引用数据库地址的值,区分敏感数据

cargo add dotenv

.env 中填写以下

DATABASE_URL=postgresql://username:password@localhost/database_name

替换上面成自己的数据库url地址。

添加依赖

[dependencies]
diesel = {"version" = "2.2.7", features = ["postgres"] }
dotenv = "0.15.0"

初始化 Diesel

diesel setup

编写 SQL 迁移文件

up.sql(创建 users 表)

migrations/create_users/up.sql 中,添加以下 SQL 语句:

CREATE TABLE users (
    id SERIAL PRIMARY KEY,
    username VARCHAR NOT NULL UNIQUE,
    email VARCHAR NOT NULL UNIQUE,
    password TEXT NOT NULL
)

down.sql(回滚操作)

如果需要回滚该迁移(撤销 users 表),在 migrations/create_users/down.sql 写入:

DROP TABLE users;

运行迁移

执行以下命令,将迁移应用到数据库:

diesel migration run

如果需要撤销迁移(回滚):

diesel migration revert

会自动产生 schema.rs 文件

// @generated automatically by Diesel CLI.
diesel::table! {
    users (id) {
        id -> Int4,
        name -> Varchar,
        password -> Varchar,
        email -> Varchar,
    }
}

手动创建 models.rs

use diesel::prelude::*;

#[derive(Queryable, Debug)]
pub struct User {
    pub id: i32,
    pub username: String,
    pub email: String,
    pub password: String,
}

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

增删改查

我们在 main.rs 里实现 CRUD 逻辑:

(1) 连接数据库

use diesel::prelude::*;
use diesel::pg::PgConnection;
use dotenvy::dotenv;
use std::env;

pub fn establish_connection() -> PgConnection {
    dotenv().ok();
    let database_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set");
    PgConnection::establish(&database_url).expect("Error connecting to database")
}

(2) 增(Create)

use crate::models::{NewUser, User};
use crate::schema::users::dsl::*;

pub fn create_user(conn: &mut PgConnection, uname: &str, mail: &str, pass: &str) -> User {
    let new_user = NewUser { username: uname, email: mail, password: pass };

    diesel::insert_into(users)
        .values(&new_user)
        .returning(User::as_returning())
        .get_result(conn)
        .expect("Error saving new user")
}

测试

fn main() {
    let mut conn = establish_connection();
    let user = create_user(&mut conn, "testuser", "test@example.com", "password123");
    println!("Created user: {:?}", user);
}

(3) 查(Read)

获取所有用户

pub fn get_all_users(conn: &mut PgConnection) -> Vec<User> {
    users.load::<User>(conn).expect("Error loading users")
}

测试

fn main() {
    let mut conn = establish_connection();
    let user_list = get_all_users(&mut conn);
    println!("Users: {:?}", user_list);
}

(4) 查(Read):获取单个用户

pub fn get_user_by_id(conn: &mut PgConnection, user_id: i32) -> Option<User> {
    users.find(user_id).first::<User>(conn).ok()
}

测试

fn main() {
    let mut conn = establish_connection();
    match get_user_by_id(&mut conn, 1) {
        Some(user) => println!("Found user: {:?}", user),
        None => println!("User not found"),
    }
}

(5) 改(Update)

use diesel::dsl::update;
pub fn update_user(conn: &mut PgConnection, user_id: i32, new_name: &str) -> usize {
    diesel::update(users.find(user_id))
        .set(username.eq(new_name))
        .execute(conn)
        .expect("Error updating user")
}

测试

fn main() {
    let mut conn = establish_connection();
    let updated_rows = update_user(&mut conn, 1, "new_username");
    println!("Updated {} rows", updated_rows);
}

(6) 删(Delete)

pub fn delete_user(conn: &mut PgConnection, user_id: i32) -> usize {
    diesel::delete(users.find(user_id))
        .execute(conn)
        .expect("Error deleting user")
}

测试

fn main() {
    let mut conn = establish_connection();
    let deleted_rows = delete_user(&mut conn, 1);
    println!("Deleted {} rows", deleted_rows);
}