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