Diesel集成 #
Diesel是Rust中最流行的ORM,提供编译时类型安全的SQL查询。本节将介绍如何在Rocket中集成Diesel。
安装配置 #
添加依赖 #
toml
[dependencies]
rocket = { version = "0.5", features = ["json"] }
rocket_sync_db_pools = { version = "0.1", features = ["diesel_postgres_pool"] }
diesel = { version = "2.1", features = ["postgres", "chrono", "uuid"] }
serde = { version = "1.0", features = ["derive"] }
[dependencies.rocket_sync_db_pools]
version = "0.1"
features = ["diesel_postgres_pool"]
安装Diesel CLI #
bash
cargo install diesel_cli --no-default-features --features postgres
配置数据库 #
toml
[default.databases.postgres]
url = "postgres://user:password@localhost/mydb"
初始化项目 #
创建数据库 #
bash
diesel setup
创建迁移 #
bash
diesel migration generate create_users
迁移文件 #
migrations/xxx_create_users/up.sql:
sql
CREATE TABLE users (
id SERIAL PRIMARY KEY,
username VARCHAR(50) NOT NULL UNIQUE,
email VARCHAR(100) NOT NULL UNIQUE,
password_hash VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
migrations/xxx_create_users/down.sql:
sql
DROP TABLE users;
运行迁移 #
bash
diesel migration run
模型定义 #
schema.rs #
rust
diesel::table! {
users (id) {
id -> Int4,
username -> Varchar,
email -> Varchar,
password_hash -> Varchar,
created_at -> Timestamp,
updated_at -> Timestamp,
}
}
模型结构 #
rust
use diesel::prelude::*;
use serde::{Serialize, Deserialize};
#[derive(Debug, Queryable, Serialize)]
pub struct User {
pub id: i32,
pub username: String,
pub email: String,
pub password_hash: String,
pub created_at: chrono::NaiveDateTime,
pub updated_at: chrono::NaiveDateTime,
}
#[derive(Debug, Insertable)]
#[diesel(table_name = crate::schema::users)]
pub struct NewUser {
pub username: String,
pub email: String,
pub password_hash: String,
}
#[derive(Debug, AsChangeset, Deserialize)]
#[diesel(table_name = crate::schema::users)]
pub struct UpdateUser {
pub username: Option<String>,
pub email: Option<String>,
}
数据库连接 #
配置连接池 #
rust
#[macro_use] extern crate rocket;
use rocket_sync_db_pools::{database, diesel};
#[database("postgres")]
struct Db(diesel::PgConnection);
#[launch]
fn rocket() -> _ {
rocket::build()
.attach(Db::fairing())
.mount("/api", routes![list_users, get_user, create_user])
}
CRUD操作 #
查询所有 #
rust
use crate::schema::users::dsl::*;
#[get("/users")]
async fn list_users(db: Db) -> Json<Vec<User>> {
db.run(|conn| {
users
.load::<User>(conn)
.expect("Error loading users")
}).await.into()
}
查询单个 #
rust
#[get("/users/<user_id>")]
async fn get_user(db: Db, user_id: i32) -> Option<Json<User>> {
db.run(move |conn| {
users
.find(user_id)
.first::<User>(conn)
.ok()
}).await.map(Json)
}
创建 #
rust
use rocket::serde::json::Json;
#[post("/users", format = "json", data = "<new_user>")]
async fn create_user(db: Db, new_user: Json<NewUser>) -> Json<User> {
db.run(|conn| {
diesel::insert_into(users)
.values(&new_user.into_inner())
.returning((id, username, email, password_hash, created_at, updated_at))
.get_result(conn)
.expect("Error creating user")
}).await.into()
}
更新 #
rust
#[put("/users/<user_id>", format = "json", data = "<update>")]
async fn update_user(
db: Db,
user_id: i32,
update: Json<UpdateUser>,
) -> Option<Json<User>> {
db.run(move |conn| {
diesel::update(users.find(user_id))
.set(&update.into_inner())
.returning((id, username, email, password_hash, created_at, updated_at))
.get_result(conn)
.ok()
}).await.map(Json)
}
删除 #
rust
use rocket::http::Status;
#[delete("/users/<user_id>")]
async fn delete_user(db: Db, user_id: i32) -> Result<Status, Status> {
let deleted = db.run(move |conn| {
diesel::delete(users.find(user_id))
.execute(conn)
.unwrap_or(0)
}).await;
if deleted > 0 {
Ok(Status::NoContent)
} else {
Err(Status::NotFound)
}
}
复杂查询 #
条件查询 #
rust
#[get("/users/search?<q>")]
async fn search_users(db: Db, q: &str) -> Json<Vec<User>> {
let pattern = format!("%{}%", q);
db.run(move |conn| {
users
.filter(username.like(pattern.clone())
.or(email.like(pattern)))
.load::<User>(conn)
.expect("Error searching users")
}).await.into()
}
分页查询 #
rust
#[get("/users?<page>&<per_page>")]
async fn list_users_paginated(
db: Db,
page: Option<i64>,
per_page: Option<i64>,
) -> Json<Vec<User>> {
let page = page.unwrap_or(1);
let per_page = per_page.unwrap_or(10);
let offset = (page - 1) * per_page;
db.run(move |conn| {
users
.order(created_at.desc())
.limit(per_page)
.offset(offset)
.load::<User>(conn)
.expect("Error loading users")
}).await.into()
}
关联查询 #
rust
diesel::table! {
posts (id) {
id -> Int4,
title -> Varchar,
content -> Text,
user_id -> Int4,
created_at -> Timestamp,
}
}
diesel::joinable!(posts -> users (user_id));
#[derive(Debug, Queryable, Serialize)]
pub struct PostWithUser {
pub id: i32,
pub title: String,
pub content: String,
pub user_id: i32,
pub username: String,
}
#[get("/posts")]
async fn list_posts_with_users(db: Db) -> Json<Vec<PostWithUser>> {
use crate::schema::posts::dsl as p;
use crate::schema::users::dsl as u;
db.run(|conn| {
p::posts
.inner_join(u::users)
.select((p::id, p::title, p::content, p::user_id, u::username))
.load::<PostWithUser>(conn)
.expect("Error loading posts")
}).await.into()
}
完整示例 #
rust
#[macro_use] extern crate rocket;
use rocket::serde::json::Json;
use rocket::serde::{Serialize, Deserialize};
use rocket_sync_db_pools::{database, diesel};
use diesel::prelude::*;
mod schema {
diesel::table! {
users (id) {
id -> Int4,
username -> Varchar,
email -> Varchar,
created_at -> Timestamp,
}
}
}
#[database("postgres")]
struct Db(diesel::PgConnection);
use schema::users::dsl::*;
#[derive(Debug, Queryable, Serialize)]
struct User {
id: i32,
username: String,
email: String,
created_at: chrono::NaiveDateTime,
}
#[derive(Debug, Insertable, Deserialize)]
#[diesel(table_name = schema::users)]
struct NewUser {
username: String,
email: String,
}
#[get("/users")]
async fn list(db: Db) -> Json<Vec<User>> {
db.run(|c| users.load::<User>(c).unwrap())
.await.into()
}
#[get("/users/<user_id>")]
async fn get(db: Db, user_id: i32) -> Option<Json<User>> {
db.run(move |c| users.find(user_id).first::<User>(c).ok())
.await.map(Json)
}
#[post("/users", format = "json", data = "<user>")]
async fn create(db: Db, user: Json<NewUser>) -> Json<User> {
db.run(|c| {
diesel::insert_into(users)
.values(&user.into_inner())
.returning((id, username, email, created_at))
.get_result(c)
.unwrap()
}).await.into()
}
#[delete("/users/<user_id>")]
async fn delete(db: Db, user_id: i32) -> &'static str {
db.run(move |c| {
diesel::delete(users.find(user_id)).execute(c).unwrap();
}).await;
"Deleted"
}
#[launch]
fn rocket() -> _ {
rocket::build()
.attach(Db::fairing())
.mount("/api", routes![list, get, create, delete])
}
下一步 #
掌握了Diesel集成后,让我们继续学习 SQLx集成,了解另一种数据库访问方式。
最后更新:2026-03-28