Rustの新しいORM「SeaORM」を試す
Rust ではながらく ORM としては Diesel しか選択肢がなかったが、async 対応がまだなど少し課題を抱えていた。最近になって新しく ORM が登場してきて、選択肢が増えた。それを触ってみる。
バックエンドに sqlx を使用しているらしく、データベースドライバはそれ経由で選べる。
特徴的なのは非同期処理ランタイムが複数対応している点だと思う。actix 向け、tokio 向け、そして async-std 向けの3つに対応しているようだ。
Diesel はコンパイルタイムでできる限りのことを解決するスタイルで、SeaORM はランタイムで解決する方に倒している感じらしい。静的 vs 動的、な違いがあるみたい。動的側に倒すとパフォーマンスは多少劣化するが、型解決が楽になるなどのメリットがいくつかある。「使いやすい方」に倒しているライブラリだと思われる。
ようやくMySQLに接続できた。下記のようなサンプルの MySQL サーバーを立てて起動してみたところ、接続できたらしい。
use sea_orm::Database;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let db = Database::connect("mysql://user:password@localhost:3306/test").await?;
Ok(())
}
docker-compose の設定。
version: "3.9"
services:
db:
image: mysql:5.7
volumes:
- ./db_data:/var/lib/mysql
restart: always
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: test
MYSQL_USER: user
MYSQL_PASSWORD: password
command: mysqld --explicit_defaults_for_timestamp
ports:
- 3306:3306
volumes:
db_data: {}
うーん、なんか期待していろいろ書いてみてるけど、正直 Diesel の方が100倍くらいわかりやすいかな。。。
Slick に近い。Slick を思い出してだいぶ厳しい気持ちになっている。
まず軽くエンティティを用意しようと思っただけなのに、ボイラープレートがここまで発生する。
use sea_orm::entity::prelude::*;
#[derive(Copy, Clone, Default, Debug, DeriveEntity)]
pub struct Entity;
impl EntityName for Entity {
fn table_name(&self) -> &str {
"room"
}
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
pub enum Column {
Id,
Name,
}
#[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)]
pub enum PrimaryKey {
Id,
}
impl PrimaryKeyTrait for PrimaryKey {
type ValueType = String;
fn auto_increment() -> bool {
false
}
}
impl ColumnTrait for Column {
type EntityName = Entity;
fn def(&self) -> ColumnDef {
match self {
Self::Id => ColumnType::Integer.def(),
Self::Name => ColumnType::String(None).def(),
}
}
}
#[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel)]
pub struct Model {
#[sea_orm(primary_key)]
pub id: String,
pub name: String,
}
impl ActiveModelBehavior for ActiveModel {}
欲しいのは、単に下記の構造体に対応するテーブルなだけなんだけど。今のところリレーションは不要だし、単に find_by_id したいだけなのに、ここまでのボイラープレートが求められてしまうとちょっと…
pub struct Room {
id: String, // これがプライマリキー
name: String
}
エンティティのボイラープレート生成がなんとかなればあとは普通の ORM なので、そうした ORM 的なよさを期待して使うのはよさそうな気はする。デフォルトで async/await 対応しているし。
Hi Yuki, thank you for trying our library. I'm one of the core member in sea-orm. Just to let you know that we have introduced the compact entity format which greatly reduce the amount of boilerplate codes.
Demo: compact format vs expanded format
For more details please check the documentation.
Hi, thank you for your comment! I missed the demo. I'll check it.