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ại
  • unit_status: Số lượng trên mõi trạng thái của mỗi product. Nó có thể là available, on hold, or pending 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