SeaORM でテーブル作成とデータ操作
github で SeaORM という Rust 用の ORM を見つけたので軽く試してみました。
sea-orm-cli というツールを使うと、既存のテーブルから Entity 定義を自動生成してくれるようですが、ここでは自前で定義した Entity を基にテーブル作成とデータ操作(INSERT, SELECT)を実施してみました。
今回のソースは こちら
1. はじめに
Cargo.toml へ依存定義を設定します。
sea-orm の features
で DB のドライバーと非同期ランタイムを指定する事になります。
今回は MySQL/MariaDB へ接続するため DB ドライバーは sqlx-mysql
、
非同期ランタイムは async-std で TLS の Rust 実装を用いる事にしたので runtime-async-std-rustls
としています。
Cargo.toml
・・・ [dependencies] sea-orm = { version = "0.8", features = ["sqlx-mysql", "runtime-async-std-rustls", "macros" ] } async-std = { version = "1", features = ["attributes"] }
2. Entity 定義
ここでは、以下のようなテーブル内容を想定した Entity 定義を行います。
CREATE TABLE tasks ( id int(10) unsigned NOT NULL AUTO_INCREMENT, subject varchar(255) NOT NULL, status enum('ready','completed') NOT NULL, PRIMARY KEY (id) )
Entity 定義は DeriveEntityModel
を derive した Model
という名の struct を定義すれば良さそうです。
そうすると、DeriveRelation
を derive した Relation
enum や impl ActiveModelBehavior for ActiveModel
の定義が必要となりますが、今回は特に使わないので空実装としておきます。
status カラムを DB の enum 型とするため、DeriveActiveEnum
を derive した Status enum を別途定義しています。
あとは、sea_orm
でテーブル名やプライマリキー、status カラムの enum 値(DB 側)等の指定を行っています。
task.rs
use sea_orm::entity::prelude::*; #[derive(Clone, Debug, PartialEq, DeriveEntityModel)] #[sea_orm(table_name = "tasks")] pub struct Model { #[sea_orm(primary_key)] pub id: u32, pub subject: String, pub status: Status, } #[derive(Clone, Debug, PartialEq, EnumIter, DeriveActiveEnum)] #[sea_orm(rs_type = "String", db_type = "Enum", enum_name = "status")] pub enum Status { #[sea_orm(string_value = "ready")] Ready, #[sea_orm(string_value = "completed")] Completed, } #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] pub enum Relation {} impl ActiveModelBehavior for ActiveModel {}
3. 処理の実装
DB 接続
Database::connect
へ DB の接続文字列 mysql://user:password@host/db
を渡す事で DB へ接続します。
ここでは、環境変数から DB の接続文字列を取得するようにしてみました。
let db_uri = env::var("DB_URI")?; let db = Database::connect(db_uri).await?;
テーブル作成
create_table_from_entity
で Entity 定義から Create Table 文を作成できます。
get_database_backend
で取得したバックエンドの build
を用いる事で、
接続する DB(今回は MySQL/MariaDB)用の Create Table 文を取得できるので execute
でテーブル作成を実施します。
let backend = db.get_database_backend(); let schema = Schema::new(backend); let st = backend.build(&schema.create_table_from_entity(Task)); db.execute(st).await?;
INSERT
INSERT する Entity データの生成に ActiveModel
を使い、値を ActiveValue
で設定します。
id は自動採番を使うため NotSet
としています。(明示的に設定しても可)
ActiveModel の insert
を呼び出す事で INSERT を実施します。
let t1 = task::ActiveModel { id: ActiveValue::NotSet, subject: ActiveValue::Set("task1".to_owned()), status: ActiveValue::Set(task::Status::Ready), }; let r1 = t1.insert(&db).await?; println!("{:?}", r1);
なお、serde_json と ActiveModel::from_json
を使う事で、JSON から生成する事も可能でした。
SELECT
find()
で SELECT を実施します。
all
を使う事で対象となる全レコードを取得できるようです。
let rows = Task::find().all(&db).await?; println!("{:?}", rows);
上記処理を合わせた、最終的なコードは以下のようになりました。
main.rs
mod task; use sea_orm::*; use std::env; use task::Entity as Task; type Error = Box<dyn std::error::Error>; #[async_std::main] async fn main() -> Result<(), Error> { let db_uri = env::var("DB_URI")?; let db = Database::connect(db_uri).await?; let backend = db.get_database_backend(); let schema = Schema::new(backend); let st = backend.build(&schema.create_table_from_entity(Task)); db.execute(st).await?; let t1 = task::ActiveModel { id: ActiveValue::NotSet, subject: ActiveValue::Set("task1".to_owned()), status: ActiveValue::Set(task::Status::Ready), }; let r1 = t1.insert(&db).await?; println!("{:?}", r1); let t2 = task::ActiveModel { id: ActiveValue::NotSet, subject: ActiveValue::Set("task2".to_owned()), status: ActiveValue::Set(task::Status::Completed), }; let r2 = t2.insert(&db).await?; println!("{:?}", r2); let rows = Task::find().all(&db).await?; println!("{:?}", rows); Ok(()) }
4. 動作確認
MariaDB へ DB を作成しておきます。
DB 作成
MariaDB [(none)]> CREATE DATABASE sample1;
環境変数へ DB 接続文字列を設定し、実行します。
実行
> set DB_URI=mysql://root:@localhost/sample1 > cargo run ・・・ Model { id: 1, subject: "task1", status: Ready } Model { id: 2, subject: "task2", status: Completed } [Model { id: 1, subject: "task1", status: Ready }, Model { id: 2, subject: "task2", status: Completed }]
正常に実行できました。
ついでに、テーブルとレコード内容を確認してみると以下のようになりました。
テーブルとレコード内容
MariaDB [sample1]> SHOW CREATE TABLE tasks \G *************************** 1. row *************************** Table: tasks Create Table: CREATE TABLE `tasks` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `subject` varchar(255) NOT NULL, `status` enum('ready','completed') NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1 1 row in set (0.000 sec) MariaDB [sample1]> select * from tasks; +----+---------+-----------+ | id | subject | status | +----+---------+-----------+ | 1 | task1 | ready | | 2 | task2 | completed | +----+---------+-----------+ 2 rows in set (0.001 sec)