Upgrade to Pro — share decks privately, control downloads, hide ads and more …

SeaQL Projectsについて

Yuki Toyoda
June 05, 2024
430

SeaQL Projectsについて

Yuki Toyoda

June 05, 2024
Tweet

Transcript

  1. SeaORM / SeaORM X 一番知名度があるかもしれない、 ORM のクレート。近年話題。 MySQL、 Postgres、 SQLite

    に対応している。 「 X」の方は SQLServer 対応のもの。 Rails の Active Record に使用感は近そうに感じる。
  2. SeaORM Pros 普通に実用的なデザインをしている。 Rust にはまだ数少ない O/R マッパーを利用できる。 sea-orm-cli と組み合わせるとマイグレーションもスムーズ。 Cons

    手続き的マクロで生成される箇所のコンパイルエラーの読み解きが難しい。 Rust 1.78 から入った #[diagnostic::on_unimplemented] とかをもうちょ っと活用してよくできるかも。期待。 ボイラープレート多い。 O/R マッパーはいろいろ設定が必要な手前、仕方ない気もする。
  3. モデルの定義 : customer モデルの定義 // src/customer.rs use sea_orm::entity::prelude::*; #[derive(Clone, Debug,

    PartialEq, DeriveEntityModel)] #[sea_orm(table_name = "customers")] pub struct Model { #[sea_orm(primary_key, auto_increment = true)] pub customer_id: i32, pub name: String, pub address: String, pub phone_number: String, } #[derive(Clone, Debug, EnumIter, DeriveRelation)] pub enum Relation { #[sea_orm(has_many = "super::order::Entity")] Order, } impl Related<super::order::Entity> for Entity { fn to() -> RelationDef { Relation::Order.def() } } impl ActiveModelBehavior for ActiveModel {}
  4. エンティティとモデル // DeriveEntityModelでカラムやプライマリキーに関する情報が埋め合わせされる。 #[derive(Clone, Debug, DeriveEntityModel)] #[sea_orm(table_name = "customers")] //

    構造体の名前は`Model`じゃないとダメ。 pub struct Model { #[sea_orm(primary_key, auto_increment = true)] pub customer_id: i32, pub name: String, pub address: String, pub phone_number: String, } // insertやupdateなどのメソッドを生やしてくれる`ActiveModel`。 impl ActiveModelBehavior for ActiveModel { // ActiveModelBehaviorを使うと、保存や削除処理をする直前に、 // たとえばバリデーションチェックなどの機構を挟み込むことができる。 // 何もしない場合は空にしておく(飛ばすことはできない) 。 }
  5. モデル定義 : order モデルの定義 // src/order.rs use sea_orm::entity::prelude::*; #[derive(Clone, Debug,

    DeriveEntityModel)] #[sea_orm(table_name = "orders")] pub struct Model { #[sea_orm(primary_key, auto_increment = true)] pub order_id: i32, pub ordered_at: DateTime, pub customer_id: i32, pub item_id: i32, pub amount: u32, } #[derive(Clone, Debug, EnumIter, DeriveRelation)] pub enum Relation { #[sea_orm( belongs_to = "super::customer::Entity", from = "Column::CustomerId", to = "super::customer::Column::CustomerId" )] Customer, #[sea_orm( belongs_to = "super::item::Entity", from = "Column::ItemId", to = "super::item::Column::ItemId" )] Item, } impl Related<super::customer::Entity> for Entity { fn to() -> RelationDef { Relation::Customer.def() } } impl Related<super::item::Entity> for Entity { fn to() -> RelationDef { Relation::Item.def() } } impl ActiveModelBehavior for ActiveModel {}
  6. モデルの定義 : item モデルの定義 // src/item.rs use sea_orm::entity::prelude::*; #[derive(Clone, Debug,

    DeriveEntityModel)] #[sea_orm(table_name = "items")] pub struct Model { #[sea_orm(primary_key, auto_increment = true)] pub item_id: i32, pub name: String, pub price: u32, } #[derive(Clone, Debug, EnumIter, DeriveRelation)] pub enum Relation { #[sea_orm(has_many = "super::order::Entity")] Order, } impl Related<super::order::Entity> for Entity { fn to() -> RelationDef { Relation::Order.def() } } impl ActiveModelBehavior for ActiveModel {}
  7. リレーションの定義 customer と order は one-to-many の関係性にする。 item と order

    は one-to-many の関係性にする。 これを SeaORM 上で定義する。
  8. customer に実装する #[derive(Clone, Debug, EnumIter, DeriveRelation)] pub enum Relation {

    // customer -> orderに対する関係性を定義する。 #[sea_orm(has_many = "super::order::Entity")] Order, } // customer::Entityに対して関係性情報を追加する。 impl Related<super::order::Entity> for Entity { fn to() -> RelationDef { Relation::Order.def() } }
  9. item に実装する customer と同様。 #[derive(Clone, Debug, EnumIter, DeriveRelation)] pub enum

    Relation { #[sea_orm(has_many = "super::order::Entity")] Order, } impl Related<super::order::Entity> for Entity { fn to() -> RelationDef { Relation::Order.def() } }
  10. order に実装する #[derive(Clone, Debug, EnumIter, DeriveRelation)] pub enum Relation {

    // どのカラムをキーとしてマッピングさせるかを定義する。 // itemとcustomerに対して定義する。 #[sea_orm( belongs_to = "super::customer::Entity", from = "Column::CustomerId", to = "super::customer::Column::CustomerId" )] Customer, #[sea_orm( belongs_to = "super::item::Entity", from = "Column::ItemId", to = "super::item::Column::ItemId" )] Item, } // order::Entityとcustomer/item::Entityの関係性を定義する。 impl Related<super::customer::Entity> for Entity { fn to() -> RelationDef { Relation::Customer.def() } } impl Related<super::item::Entity> for Entity { fn to() -> RelationDef { Relation::Item.def() } }
  11. テストコード #[cfg(test)] mod tests { use sea_orm::{entity::prelude::*, DatabaseBackend, MockDatabase}; use

    crate::customer::Entity as CustomerEntity; use crate::{ customer::Model as CustomerModel, item::Model as ItemModel, order::Model as OrderModel, }; #[tokio::test] async fn test_find_item() -> Result<(), DbErr> { let db = MockDatabase::new(DatabaseBackend::Postgres) .append_query_results([vec![CustomerModel { /* 省略 */ }]]) .append_query_results([vec![ItemModel { /* 省略 */ }]]) .append_query_results([vec![OrderModel { /* 省略 */ }]]) .into_connection(); assert_eq!( CustomerEntity::find_by_id(1).one(&db).await?, Some(CustomerModel { /* 省略 */ }) ); Ok(()) } }
  12. CRUD 関数 生成した Entity に対して、 find 、 insert 、 update

    、 delete などの関数が生え てくる。 これを使ってデータベース操作を行う。 CustomerEntity::find_by_id(1).one(&db).await?, Some(CustomerModel { /* 省略 */ })