SeaORM is a relational ORM that helps you
build web services in Rust using a familiar dynamic language.
The official documentation for SeaORM is on their official website .
This guide uses PostgreSQL . Before we begin, make sure you
have PostgreSQL installed for your system .
In this tutorial, we'll build simple examples of each CRUD operation.
I'm using Rust v1.62 and sea-orm
v0.9
Initialize a new project
cargo new seaorm_demo --lib
cd seaorm_demo
First, let's add SeaORM and tokio to our dependencies.
document:Cargo.toml
[dependencies]
tokio = { version = "1.20", features = ["macros", "rt-multi-thread"] }
[dependencies.sea-orm]
version = "0.9"
features = [ "sqlx-postgres", "runtime-tokio-rustls", "macros" ]
default-features = false
Install sea-orm-cli and migrate
cargo install sea-orm-cli
We'll write a migration file to set up our database and table schema.
sea-orm-cli migrate init
Next will generate a file named migration的目录
Now our project structure should look like this.
.
├── Cargo.lock
├── Cargo.toml
├── migration
│ ├── Cargo.toml
│ ├── README.md
│ └── src
│ ├── lib.rs
│ ├── m20220101_000001_create_table.rs
│ └── main.rs
└── src
└── lib.rs
Open the file migration/Cargo.toml
and uncomment the last two lines sea-orm-migration
.
document:migration/Cargo.toml
[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-postgres", # `DATABASE_DRIVER` feature
]
migration/src/m20220101_000001_create_table.rs
Edit the file in your favorite editor
and delete the two "todo!()"
saves.
Create a new " .env " file in the project root directory
DATABASE_URL="postgres://root:root@localhost:5432/axum_example"
Next, we'll run the migration.
sea-orm-cli migrate up
It will compile migration
the module and run your migrations. After this, you
should see a file called posts.pdb in your directory.
generate entity
Create a new entity
module.
cargo new entity --lib
Next, generate entities.
sea-orm-cli generate entity -o entity/src
Add sea-orm
dependencies to entity
modules.
document:entity/Cargo.toml
[dependencies]
sea-orm = { version = "0.9" }
The resulting entity should look like this.
document:entity/src/post.rs
//! SeaORM Entity. Generated by sea-orm-codegen 0.9.0
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 {}
Since the module entity
is in the root of our project, we remove lib.rs and turn mod.rs
into a library so we can use it.
Rename entity/src/mod.rs
to entity/src/lib.rs
.
mv entity/src/mod.rs entity/src/lib.rs
Next, we add entity
the and migration
library to
the root project's dependencies.
document:Cargo.toml
[workspace]
members = [".", "entity", "migration"]
[dependencies]
entity = { path = "entity" }
migration = { path = "migration" }
Now your project structure should look like this
.
├── 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
└── migration.pdb
项目Cargo.toml
It should look like this.
document:Cargo.tmol
[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-postgres", "runtime-tokio-rustls", "macros" ]
default-features = false
Connect to the database
Now we write the code to establish the connection to the database.
document:src/lib.rs
use migration::{DbErr, Migrator, MigratorTrait};
use sea_orm::{Database, DbConn};
const DATABASE_URL: &str = "postgres://root:root@localhost:5432/axum_example";
pub async fn establish_connection() -> Result<DbConn, DbErr> {
let db = Database::connect(DATABASE_URL)
.await
.expect("连接数据库失败");
Migrator::up(&db, None)
.await
.expect("迁移失败");
Ok(db)
}
"Add" - add data
Now let's write some code to create a post. Create a new file src/bin/create_post.rs
.
document: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("我是title")),
text: Set(String::from("我是text")),
..Default::default()
};
let post: post::Model = post.insert(&db).await?;
println!("ID: {}, title: {}", post.id, post.title);
Ok(())
}
We can run our new script as follows.
cargo run --bin create_post
It should look like this.
$ cargo run --bin create_post
Compiling seaorm_demo v0.1.0
Finished dev [unoptimized + debuginfo] target(s) in 3.85s
Running `target/debug/create_post`
ID: 1, title: 我是title
If you wish to create more entries in the database, change the title/text create_post.rs
and execute the script again.
I will create another one.
$ cargo run --bin create_post
Compiling seaorm_demo v0.1.0
Finished dev [unoptimized + debuginfo] target(s) in 4.08s
Running `target/debug/create_post`
ID: 2, title: 我是title
"check" - form query
Next, we write an example that reads all posts in the database.
document: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!("表中的所有帖子:");
for post in posts {
println!("id: {}, title: {}", post.id, post.title);
}
Ok(())
}
Just like before, you can run this new file as follows.
cargo run --bin read_posts
It should look like this.
$ cargo run --bin read_posts
Compiling seaorm_demo v0.1.0
Finished dev [unoptimized + debuginfo] target(s) in 4.08s
Running `target/debug/read_posts`
表中的所有帖子:
ID: 1, title : 我是title
ID: 2, title : 我是title
"Change" - update data
Now, suppose we want to perform an UPDATE operation on the title of the post.
document: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?;
//根据ID更新帖子内容
let post = post::Entity::find_by_id(1).one(&db).await?;
let mut post: post::ActiveModel = post.unwrap().into();
post.title = Set("哈哈,我被更新啦".to_owned());
let post: post::Model = post.update(&db).await?;
println!("更新后的帖子id: {} title: {}", post.id, post.title);
Ok(())
}
we run this script
cargo run --bin update_post
It should look like this.
$ cargo run --bin update_post ⏎
Compiling seaorm_demo v0.1.0
Finished dev [unoptimized + debuginfo] target(s) in 2.80s
Running `target/debug/update_post`
更新后的帖子id:1 title: 哈哈,我被更新啦
"delete" - delete data
Now for the final operation, delete. create a new filesrc/bin/delete_post.rs
We will delete post with id 1
document: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(())
}
We will call this script
cargo run --bin delete_post
It should look like this.
$ cargo run --bin delete_post
Compiling seaorm_demo v0.1.0
Finished dev [unoptimized + debuginfo] target(s) in 5.42s
Running `target/debug/delete_post`
DeleteResult { rows_affected: 1 }
We can execute the script again read_post
to see what other posts are in the database.
$ cargo run --bin read_posts
Finished dev [unoptimized + debuginfo] target(s) in 0.31s
Running `target/debug/read_posts`
ID: 2, title: 我是title