本記事では、SeaORMとOceanBaseデータベースを使用してアプリケーションを構築し、テーブルの作成、データの挿入、クエリなどの基本的な操作を実行する方法を紹介します。
環境の準備
開始する前に、以下のツールがインストールされていることを確認してください:
- Rust 1.60以降のバージョン
- Cargo(Rustのパッケージマネージャー)
- OceanBaseデータベースV4.2.4以降のバージョン(MySQLモード)がインストールされ、実行されていること
新しいプロジェクトの作成
cargo new sea-orm-oceanbase-demo
cd sea-orm-oceanbase-demo
依存関係の追加
Cargo.toml ファイルを編集し、以下の依存関係を追加します:
[package]
name = "sea-orm-oceanbase-demo"
version = "0.1.0"
edition = "2021"
[dependencies]
tokio = { version = "1.0", features = ["full"] }
sea-orm = { version = "0.12", features = [
"sqlx-mysql",
"runtime-tokio-rustls",
"macros",
"with-json"
] }
sea-orm-migration = "0.12"
serde = { version = "1.0", features = ["derive"] }
dotenv = "0.15"
async-std = { version = "1.12", features = ["attributes"] }
chrono = "0.4"
データベース接続の設定
プロジェクトのルートディレクトリに .env ファイルを作成します:
DATABASE_URL=mysql://username:password@localhost:2881/database_name
エンティティの定義
src/entities ディレクトリを作成し、mod.rs と user.rs ファイルを追加します:
// src/entities/mod.rs
pub mod user;
// src/entities/user.rs
use sea_orm::entity::prelude::*;
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Serialize, Deserialize)]
#[sea_orm(table_name = "users")]
pub struct Model {
#[sea_orm(primary_key)]
pub id: i32,
pub name: String,
pub email: String,
pub created_at: DateTimeUtc,
pub updated_at: DateTimeUtc,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {}
impl ActiveModelBehavior for ActiveModel {}
データベース接続
src/db.rs を作成します:
use sea_orm::*;
use std::env;
use dotenv::dotenv;
pub async fn establish_connection() -> Result<DatabaseConnection, DbErr> {
dotenv().ok();
let database_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set");
Database::connect(&database_url).await
}
CRUD操作の実装
src/user_service.rs を作成します:
use crate::entities::user;
use sea_orm::*;
use chrono::Utc;
pub struct UserService;
impl UserService {
// ユーザーの作成
pub async fn create_user(
db: &DatabaseConnection,
name: String,
email: String,
) -> Result<user::Model, DbErr> {
let new_user = user::ActiveModel {
name: Set(name),
email: Set(email),
created_at: Set(Utc::now()),
updated_at: Set(Utc::now()),
..Default::default()
};
new_user.insert(db).await
}
// 単一ユーザーの取得
pub async fn get_user(
db: &DatabaseConnection,
id: i32,
) -> Result<Option<user::Model>, DbErr> {
user::Entity::find_by_id(id).one(db).await
}
// ユーザーの更新
pub async fn update_user(
db: &DatabaseConnection,
id: i32,
name: Option<String>,
email: Option<String>,
) -> Result<user::Model, DbErr> {
let mut user: user::ActiveModel = user::Entity::find_by_id(id)
.one(db)
.await?
.ok_or_else(|| DbErr::Custom("User not found".to_owned()))?
.into();
if let Some(name) = name {
user.name = Set(name);
}
if let Some(email) = email {
user.email = Set(email);
}
user.updated_at = Set(Utc::now());
user.update(db).await
}
// ユーザーの削除
pub async fn delete_user(
db: &DatabaseConnection,
id: i32,
) -> Result<DeleteResult, DbErr> {
let user: user::ActiveModel = user::Entity::find_by_id(id)
.one(db)
.await?
.ok_or_else(|| DbErr::Custom("User not found".to_owned()))?
.into();
user.delete(db).await
}
// 全ユーザーの取得
pub async fn get_all_users(
db: &DatabaseConnection,
) -> Result<Vec<user::Model>, DbErr> {
user::Entity::find().all(db).await
}
}
メインプログラム
src/main.rs を修正します:
mod entities;
mod db;
mod user_service;
use sea_orm::{DatabaseConnection, DbErr, Statement, ConnectionTrait};
use user_service::UserService;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// データベース接続の確立
println!("Connecting to database...");
let db = db::establish_connection().await?;
println!("Database connected successfully!");
// テーブルの作成(存在しない場合)
println!("Creating tables if not exist...");
create_tables(&db).await?;
println!("Tables created successfully!");
// ユーザーの作成
println!("Inserting sample data...");
let users_to_create = vec![
("Alice", "alice@example.com"),
("Bob", "bob@example.com"),
("Charlie", "charlie@example.com"),
];
for (name, email) in users_to_create {
println!("Creating user: {} <{}>", name, email);
match UserService::create_user(
&db,
name.to_string(),
email.to_string()
).await {
Ok(user) => println!("Created user: {} <{}>", user.name, user.email),
Err(e) => eprintln!("Error creating user: {}", e),
}
}
// 全ユーザーの取得と出力
println!("\nCurrent users in database:");
match UserService::get_all_users(&db).await {
Ok(users) => {
for user in users {
println!("ID: {}, Name: {}, Email: {}, Created: {}, Updated: {}",
user.id, user.name, user.email, user.created_at, user.updated_at);
}
},
Err(e) => eprintln!("Error fetching users: {}", e),
}
Ok(())
}
async fn create_tables(db: &DatabaseConnection) -> Result<(), DbErr> {
let stmt = r#"
CREATE TABLE IF NOT EXISTS users (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL,
email VARCHAR(255) NOT NULL UNIQUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
"#;
db.execute(Statement::from_string(
db.get_database_backend(),
stmt.to_owned(),
))
.await?;
Ok(())
}
プログラムの実行
cargo run
期待される実行結果は以下のとおりです:
Connecting to database...
Database connected successfully!
Creating tables if not exist...
Tables created successfully!
Inserting sample data...
Creating user: Alice <alice@example.com>
Creating user: Bob <bob@example.com>
Creating user: Charlie <charlie@example.com>
Current users in database:
ID: 1, Name: Alice, Email: alice@example.com, Created: 2025-05-27 09:47:01 UTC, Updated: 2025-05-27 09:47:01 UTC
ID: 2, Name: Bob, Email: bob@example.com, Created: 2025-05-27 09:47:01 UTC, Updated: 2025-05-27 09:47:01 UTC
ID: 3, Name: Charlie, Email: charlie@example.com, Created: 2025-05-27 09:47:01 UTC, Updated: 2025-05-27 09:47:01 UTC