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