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);
}