[Rust Serial] Bài 7: Database in Rust: SeaORM - Diesel and Prisma
Bài này chúng ta sẽ tìm hiểu về một số thư viện thao tác database trong Rust
Outline
- Chapter 0: Định nghĩa bài toán
- Chapter 1: Sử dụng Prisma
- Chapter 2: Sử dụng Diesel
- Chapter 3: Sử dụng Seaorm
- Chapter 4: My Choice?
References:
- https://www.reddit.com/r/rust/comments/18vzkfi/community_review_on_rust_database_orm_crates/
Chapter 0:Định nghĩa bài toán: Music Store
Chúng ta có 4 đối tượng như sau
products
: sẽ bao gồm stock hiện tại của mỗi CD bao gồm tên, artist, số lượng còn lạiunit_status
: Số lượng trên mõi trạng thái của mỗiproduct
. Nó có thể làavailable
,on hold
, orpending shipment
.customer_transactions
: Bò gồm các bản ghi khi một order được tạo ra.customers
: Danh sach khách hàng đã mua CDs
Các khia cạnh chúng ta cần quan tâm
- Database first: Chúng ta tạo database, thiết kế các bảng bằng công cụ khác nhau sau đó database tools sẽ tạo ra các struture tương ứng với database. Ngoài ra chúng còn tạo các hàm để thao tác
- Structure first: Chúng ta tạo structure trong code để mô tả các bảng và sử dụng nó để phản ánh vào trong database
Các vấn đề quan tâm khi thao tác với database
- Migration
- Mock Testing
- Query
Chapter 1: Sử dụng Prisma Client
Dạng database first
Các bước để thao tác chính như sau:
- Sử dụng
schema.prisma
để thiết kế database - Sử dụng
prisma generate
áp dụng tới database, nó cũng sinh ra một file output là prisma client, ngoài ra nó sinh ra 1 thư mục migration chưa các migrations cho mình - Sử dụng file prisma client này trong dự án
Eg: ![Link][https://github.com/bichkhe/bid-car-identity]
Migration
- Sửa
schema.prisma
- Sử dụng command
prisma migrate
để tạo thêm 1 migrate
Chapter 2: Sử dụng Diesel
Các bước thực hiện
Install diesel cli
sudo apt-get install libpq-dev
cargo install diesel_cli --no-default-features --features postgres
Create a migration
diesel setup
diesel migration generate create_user > /dev/null 2>&1
Runa migration
export DATABASE_URL=postgres://useer:password@localhost:6432/rust-arch
diesel migration run
Run and watch
cargo watch -x "run --release"
From existing database
diesel print-schema >> src/schema.rs
cargo install diesel_cli_ext
diesel_ext --model -t > src/models.rs
Cách sử dụng
Giả sử chúng ta sinh ra một file models.rs
với nội dung như sau
#[derive(Queryable, Debug, Insertable)]
#[diesel(table_name = users)]
pub struct User {
pub id: u32,
pub created_at: NaiveDateTime,
pub updated_at: NaiveDateTime,
pub identification_code: Option<String>,
pub last_office_id: Option<u32>,
pub email_enc: Option<Vec<u8>>,
pub master_key: Option<Vec<u8>>,
pub deleted_at: Option<NaiveDateTime>,
}
và một file
```schema.rs` với nội dung
diesel::table! {
users (id) {
id -> Unsigned<Integer>,
created_at -> Timestamp,
updated_at -> Timestamp,
#[max_length = 12]
identification_code -> Nullable<Varchar>,
last_office_id -> Nullable<Unsigned<Integer>>,
email_enc -> Nullable<Blob>,
master_key -> Nullable<Blob>,
deleted_at -> Nullable<Timestamp>,
}
}
diesel::joinable!(permissions -> users (created_by));
Để có thể sử dụng file models.rs
với struct này chúng ta
use crate::schema::users;
use crate::schema::users::dsl::*;
Cái đầu tiên
let user = users::table
.select(users::all_columns)
.load::<models::User>(&mut self.pool.get().unwrap())
.expect("error loading user");
dsl::*
sẽ load tất cả các hàm, các biến liên quan đến users. Ví dụ columns, all_columns;
Cách sử dụng với join
Khi câu lệnh cần join 2 bảng diesel hỗ trợ chúng ta tupple để trả về nhiều đối tượng liên quan
Chapter 3: Sử dụng SeaORM
- Tạo một thư mục migration
- Tạo một thư mục entity
- Sử dụng query từ thư viện sea_orm, seq_query
- Nó sử dụng
sqlx
ở dưới
Migration
- Cài đặt
sea-orm-cli
cargo install sea-orm-cli@1.0.0-rc.5
- Khởi tạo
sea-orm-cli migrate init
Một folder sẽ xuất hiện
migration
├── Cargo.toml
├── README.md
└── src
├── lib.rs # Migrator API, for integration
├── m20220101_000001_create_table.rs # A sample migration file
└── main.rs # Migrator CLI, for running manually
- Tạo migrate Sử dụng command
sea-orm-cli migrate generate <name>
use sea_orm_migration::prelude::*;
#[derive(DeriveMigrationName)]
pub struct Migration;
#[async_trait]
impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
// Replace the sample below with your own migration scripts
todo!();
}
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
// Replace the sample below with your own migration scripts
todo!();
}
}
- Khởi chạy
sea-orm-cli migrate COMMAND
COMMAND: Có thể là up
, down
, status
, reset
, generate
Thực chất nó chạy
cargo run --manifest-path ./migration/Cargo.toml -- COMMAND
Giả sử chúng ta chạy
cd migration
cargo run -- up
Nó sẽ migration dựa trên migration/main.rs
Entity
sea-orm-cli generate entity -u protocol://username:password@localhost/bakery -o entity/src
Tạo một thư mục entity
- Viết một entity
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
#[sea_orm(table_name = "cake")]
pub struct Model {
#[sea_orm(primary_key)]
pub id: i32,
pub name: String,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {
#[sea_orm(has_many = "super::fruit::Entity")]
Fruit,
}
impl Related<super::fruit::Entity> for Entity {
fn to() -> RelationDef {
Relation::Fruit.def()
}
}
impl ActiveModelBehavior for ActiveModel {}
My Choice ?
https://www.reddit.com/r/rust/comments/18vzkfi/community_review_on_rust_database_orm_crates/
For me, With small table using prisma
Using sea_orm
for scaleable project
Diesel
seems to be hard to use for me. Sometimes I get the missmatch data types when creating schema.rs
Besides that, using sqlx
to build. It has some fanstastic feature as compiling checking. It's useful