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

SeaQL Projectsについて

Avatar for Yuki Toyoda Yuki Toyoda
June 05, 2024
590

SeaQL Projectsについて

Avatar for Yuki Toyoda

Yuki Toyoda

June 05, 2024
Tweet

More Decks by Yuki Toyoda

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 { /* 省略 */ })