Rust ORM-SeaORM
SeaORM 是一個關係型 ORM,它可以幫助你在 Rust 中構建 web 服務。本篇文章我們將使用 SeaORM 和 SQLite 數據庫,構建一個簡單示例,包含 CRUD 操作。
初始化一個新項目
cargo new seaorm_demo --lib
首先,讓我們將 SeaORM 和 tokio 添加到依賴項中
[dependencies]
tokio = { version = "1.20", features = ["macros", "rt-multi-thread"] }
[dependencies.sea-orm]
version = "0.9"
features = [ "sqlx-sqlite", "runtime-tokio-rustls", "macros" ]
default-features = false
安裝 sea-orm-cli 和 Migration
cargo install sea-orm-cli
我們將編寫一個 migration 文件來設置數據庫和表 schema
sea-orm-cli migrate init
它將生成一個名爲 migration 的模塊,現在我們的項目結構應該是這樣的:
├── Cargo.lock
├── Cargo.toml
├── migration
│ ├── Cargo.toml
│ ├── README.md
│ └── src
│ ├── lib.rs
│ ├── m20220101_000001_create_table.rs
│ └── main.rs
└── src
└── lib.rs
打開 migration/Cargo.toml,對 sea-orm-migration 的兩個依賴項取消註釋。
[dependencies.sea-orm-migration]
version = "^0.9.0"
features = [
# Enable at least one `ASYNC_RUNTIME` and `DATABASE_DRIVER` feature if you want to run migration via CLI.
# View the list of supported features at https://www.sea-ql.org/SeaORM/docs/install-and-config/database-and-async-runtime.
# e.g.
"runtime-tokio-rustls", # `ASYNC_RUNTIME` featrure
"sqlx-sqlite", # `DATABASE_DRIVER` feature
]
編輯文件 migration/src/m20220101_000001_create_table.rs 刪除 todo!() 並保存文件。
將數據庫的 URL 設置爲一個環境變量:
export DATABASE_URL='sqlite://posts.sqlite?mode=rwc'
接下來,我們將運行 migration:
sea-orm-cli migrate up
它將編譯 migrate 模塊並運行 migrate,在這之後,你應該會看到一個名爲 posts.sqlite 的文件在你的目錄。
爲了確認我們是否已經創建了表,運行以下命令:
$ sqlite3 posts.sqlite
CREATE TABLE IF NOT EXISTS "post" ( "id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "title" text NOT NULL, "text" text NOT NULL );
生成實體
創建一個新的實體模塊
cargo new entity --lib
接下來,生成實體
sea-orm-cli generate entity -o entity/src
它將使用我們前面通過環境變量設置的 DATABASE_URL。
將 sea-orm 依賴項添加到實體模塊中:
[dependencies]
sea-orm = { version = "0.9" }
生成的實體應該如下所示 (entity/src/post.rs):
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
#[sea_orm(table_name = "post")]
pub struct Model {
#[sea_orm(primary_key)]
pub id: i32,
pub title: String,
pub text: String,
}
#[derive(Copy, Clone, Debug, EnumIter)]
pub enum Relation {}
impl RelationTrait for Relation {
fn def(&self) -> RelationDef {
panic!("No RelationDef")
}
}
impl ActiveModelBehavior for ActiveModel {}
由於 entity 位於項目的根目錄,我們將其轉換爲一個 lib,這樣我們就可以調用它。
將 entity/src/mod.rs 文件重命名爲 entity/src/lib.rs。
接下來,我們將 entity 和 migration 添加到根項目:
[workspace]
members = [".", "entity", "migration"]
[dependencies]
entity = { path = "entity" }
migration = { path = "migration" }
現在項目結構應該如下所示:
├── Cargo.lock
├── Cargo.toml
├── entity
│ ├── Cargo.toml
│ └── src
│ ├── lib.rs
│ ├── post.rs
│ └── prelude.rs
├── migration
│ ├── Cargo.lock
│ ├── Cargo.toml
│ ├── README.md
│ └── src
│ ├── lib.rs
│ ├── m20220101_000001_create_table.rs
│ └── main.rs
├── src
│ └── lib.rs
└── tasks.sqlite
還有 Cargo.toml 應該如下所示:
[package]
name = "seaorm_demo"
version = "0.1.0"
edition = "2021"
[workspace]
members = [".", "entity", "migration"]
[dependencies]
entity = { path = "entity" }
migration = { path = "migration" }
tokio = { version = "1.20", features = ["macros", "rt-multi-thread"] }
[dependencies.sea-orm]
version = "0.9"
features = [ "sqlx-sqlite", "runtime-tokio-rustls", "macros" ]
default-features = false
現在我們編寫代碼來建立數據庫的連接:
在文件 src/lib.rs 中:
use migration::{DbErr, Migrator, MigratorTrait};
use sea_orm::{Database, DbConn};
pub async fn establish_connection() -> Result<DbConn, DbErr> {
let database_url = std::env::var("DATABASE_URL").unwrap();
let db = Database::connect(&database_url)
.await
.expect("Failed to setup the database");
Migrator::up(&db, None)
.await
.expect("Failed to run migrations for tests");
Ok(db)
}
Create
現在,讓我們編寫一些代碼來創建帖子。創建一個新文件 src/bin/create_post.rs:
use migration::DbErr;
use sea_orm::{Set, ActiveModelTrait};
use seaorm_demo::establish_connection;
use entity::post;
#[tokio::main]
async fn main() -> Result<(), DbErr>{
let db = establish_connection().await?;
let post = post::ActiveModel {
title: Set(String::from("Amazing title 1")),
text: Set(String::from("Lorem ipsum dolor sit amet.")),
..Default::default()
};
let post: post::Model = post.insert(&db).await?;
println! ("Post created with ID: {}, TITLE: {}", post.id, post.title);
Ok(())
}
我們運行腳本:
記得設置環境變量:export DATABASE_URL='sqlite://posts.sqlite?mode=rwc'
cargo run --bin create_post
運行結果如下:
master:seaorm_demo Justin$ cargo run --bin create_post
Finished dev [unoptimized + debuginfo] target(s) in 0.54s
Running `target/debug/create_post`
Post created with ID: 1, TITLE: Amazing title 1
再次創建了一個:
master:seaorm_demo Justin$ cargo run --bin create_post
Compiling seaorm_demo v0.1.0 (/Users/Justin/workspace_rust_exercise/rust-diary/202207/seaorm_demo)
Finished dev [unoptimized + debuginfo] target(s) in 4.34s
Running `target/debug/create_post`
Post created with ID: 2, TITLE: Another title 2
Read
接下來,我們編寫示例來讀取數據庫中的所有帖子。創建 src/bin/read_posts.rs 文件:
use migration::DbErr;
use sea_orm::EntityTrait;
use seaorm_demo::establish_connection;
use entity::post;
#[tokio::main]
async fn main() -> Result<(), DbErr>{
let db = establish_connection().await?;
let posts: Vec<post::Model> = post::Entity::find().all(&db).await?;
println!("All the posts in db:");
for post in posts {
println!("ID: {}, TITLE: {}", post.id, post.title);
}
Ok(())
}
運行如下命令:
cargo run --bin read_posts
執行結果如下:
master:seaorm_demo Justin$ cargo run --bin read_posts
Compiling seaorm_demo v0.1.0 (/Users/Justin/workspace_rust_exercise/rust-diary/202207/seaorm_demo)
Finished dev [unoptimized + debuginfo] target(s) in 10.63s
Running `target/debug/read_posts`
All the posts in db:
ID: 1, TITLE: Amazing title 1
ID: 2, TITLE: Another title 2
Update
現在,我們對一篇文章的標題執行跟新操作。新建文件: src/bin/ update_post.rs:
use migration::DbErr;
use sea_orm::{EntityTrait, Set, ActiveModelTrait};
use seaorm_demo::establish_connection;
use entity::post;
#[tokio::main]
async fn main() -> Result<(), DbErr>{
let db = establish_connection().await?;
// UPDATE titel of Post by ID
let post = post::Entity::find_by_id(1).one(&db).await?;
let mut post: post::ActiveModel = post.unwrap().into();
post.title = Set("Updated title".to_owned());
let post: post::Model = post.update(&db).await?;
println! ("Post updated for ID: {} with TITLE: {}", post.id, post.title);
Ok(())
}
運行如下命令:
cargo run --bin update_post
執行結果如下:
master:seaorm_demo Justin$ cargo run --bin update_post
Compiling seaorm_demo v0.1.0 (/Users/Justin/workspace_rust_exercise/rust-diary/202207/seaorm_demo)
Finished dev [unoptimized + debuginfo] target(s) in 8.43s
Running `target/debug/update_post`
Post updated for ID: 1 with TITLE: Updated title
Delete
現在是最後一個操作,刪除 ID 爲 1 的帖子 。創建一個新文件 src/bin/delete_post.rs:
use migration::DbErr;
use sea_orm::{EntityTrait, DeleteResult, ModelTrait};
use seaorm_demo::establish_connection;
use entity::post;
#[tokio::main]
async fn main() -> Result<(), DbErr>{
let db = establish_connection().await?;
let post = post::Entity::find_by_id(1).one(&db).await?;
let post: post::Model = post.unwrap();
let res: DeleteResult = post.delete(&db).await?;
assert_eq!(res.rows_affected, 1);
println!("{:?}", res);
Ok(())
}
運行如下命令:
cargo run --bin delete_post
執行結果如下:
master:seaorm_demo Justin$ cargo run --bin delete_post
Compiling seaorm_demo v0.1.0 (/Users/Justin/workspace_rust_exercise/rust-diary/202207/seaorm_demo)
Finished dev [unoptimized + debuginfo] target(s) in 8.91s
Running `target/debug/delete_post`
DeleteResult { rows_affected: 1 }
我們再次執行 read_posts:
master:seaorm_demo Justin$ cargo run --bin read_posts
Finished dev [unoptimized + debuginfo] target(s) in 0.28s
Running `target/debug/read_posts`
All the posts in db:
ID: 2, TITLE: Another title 2
ID 爲 1 的帖子刪除成功。
本文翻譯自:
https://dev.to/anshulxyz/guide-to-getting-started-with-seaorm-an-orm-for-rust-2fen
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/3orOZZizPkDttE4jCh0dAg