Share
Embed
SeaQL Projectsについて
Yuki Toyoda
June 05, 2024
Transcript
34 slides
-
SeaQL Projects とは 「 SeaXXX」とつくクレート群の総称を yuki が勝手に呼んでいる。 ORM やそれに関連する GraphQL、ストリーミング処理ライブラリ、デバッガーなどを
作っている。 有名どころでは「 SeaORM」がある。 https://www.sea-ql.org/ -
SeaORM / SeaORM X 一番知名度があるかもしれない、 ORM のクレート。近年話題。 MySQL、 Postgres、 SQLite
に対応している。 「 X」の方は SQLServer 対応のもの。 Rails の Active Record に使用感は近そうに感じる。 -
SeaORM Pros 普通に実用的なデザインをしている。 Rust にはまだ数少ない O/R マッパーを利用できる。 sea-orm-cli と組み合わせるとマイグレーションもスムーズ。 Cons
手続き的マクロで生成される箇所のコンパイルエラーの読み解きが難しい。 Rust 1.78 から入った #[diagnostic::on_unimplemented] とかをもうちょ っと活用してよくできるかも。期待。 ボイラープレート多い。 O/R マッパーはいろいろ設定が必要な手前、仕方ない気もする。 -
モデルの定義 : 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 {} -
エンティティとモデル // 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を使うと、保存や削除処理をする直前に、 // たとえばバリデーションチェックなどの機構を挟み込むことができる。 // 何もしない場合は空にしておく(飛ばすことはできない) 。 } -
モデル定義 : 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 {} -
モデルの定義 : 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 {} -
リレーションの定義 customer と order は one-to-many の関係性にする。 item と order
は one-to-many の関係性にする。 これを SeaORM 上で定義する。 -
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() } } -
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() } } -
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() } } -
テストコード #[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(()) } } -
CRUD 関数 生成した Entity に対して、 find 、 insert 、 update
、 delete などの関数が生え てくる。 これを使ってデータベース操作を行う。 CustomerEntity::find_by_id(1).one(&db).await?, Some(CustomerModel { /* 省略 */ }) -
モックデータベースの定義 MockDatabase ならびに付随する関数を利用して、モック用のデータを結構手軽にセッ トできる。便利。 let db = MockDatabase::new(DatabaseBackend::Postgres) .append_query_results([vec![CustomerModel {
/* 省略 */ }]]) .append_query_results([vec![ItemModel { /* 省略 */ }]]) .append_query_results([vec![OrderModel { /* 省略 */ }]]) .into_connection();