本記事では、RustのDiesel ORMフレームワークを使用してOceanBaseデータベースに接続し、基本的なCRUD操作を実装する方法について説明します。
環境の準備
開始する前に、以下のツールがインストールされていることを確認してください:
- Rust 1.78 以降のバージョン
- Cargo(Rustのパッケージマネージャー)
- Diesel CLI ツール
- OceanBase データベース V4.2.4 以降のバージョン(MySQLモード)がインストールされ、実行されていること
- MySQL 5.7 クライアント開発ライブラリ
MySQL 5.7 クライアント開発ライブラリのインストール
rpm -ivh https://dev.mysql.com/get/mysql57-community-release-el7-11.noarch.rpm
rpm --import https://repo.mysql.com/RPM-GPG-KEY-mysql-2022
yum install -y mysql-community-devel
Diesel CLI のインストール
# MySQLサポート付き Diesel CLI をインストール
cargo install diesel_cli --no-default-features --features mysql
新しいプロジェクトの作成
cargo new diesel-oceanbase-demo
cd diesel-oceanbase-demo
依存関係の追加
Cargo.toml ファイルを編集し、以下の依存関係を追加します:
[package]
name = "diesel-oceanbase-demo"
version = "0.1.0"
edition = "2024"
[lib]
name = "diesel_oceanbase_demo" # 注意:ハイフンではなくアンダースコアを使用します
path = "src/lib.rs"
[dependencies]
diesel = { version = "2.1.0", features = ["mysql", "r2d2", "chrono", "serde_json"] }
dotenv = "0.15"
log = "0.4"
pretty_env_logger = "0.5"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
データベースの準備
環境変数の設定:
# プロジェクトのルートディレクトリに.envファイルを作成 echo 'DATABASE_URL=mysql://username:password@localhost:2881/diesel_demo' > .envデータベースの初期化:
# データベースを作成し、テーブル構造を設定 diesel setupテーブルファイルの作成と記述:
diesel migration generate create_users期待される出力:
Creating migrations/2025-05-30-042428_create_users/up.sql Creating migrations/2025-05-30-042428_create_users/down.sql生成されたマイグレーションファイル
migrations/YYYY-MM-DD-HHMMSS_create_users/up.sqlを編集します:CREATE TABLE IF NOT EXISTS users ( id INT AUTO_INCREMENT PRIMARY KEY, username VARCHAR(50) NOT NULL, email VARCHAR(100) NOT NULL UNIQUE, active BOOLEAN NOT NULL DEFAULT TRUE );migrations/YYYY-MM-DD-HHMMSS_create_users/down.sqlを編集します:DROP TABLE users;`users テーブルを初期化します:
diesel migration runsrc/schema.rsファイルを作成し、以下の内容を記述します:diesel::table! { users (id) { id -> Integer, username -> Varchar, email -> Varchar, active -> Bool, } }
モデルの定義
src/models.rs ファイルを作成します:
use serde::{Serialize, Deserialize};
use diesel::prelude::*;
#[derive(Queryable, Selectable, Insertable, AsChangeset, Debug, Serialize, Deserialize)]
#[diesel(table_name = crate::schema::users)]
pub struct User {
pub id: i32,
pub username: String,
pub email: String,
pub active: bool,
}
#[derive(Insertable, Debug, Serialize, Deserialize)]
#[diesel(table_name = crate::schema::users)]
pub struct NewUser<'a> {
pub username: &'a str,
pub email: &'a str,
pub active: bool,
}
#[derive(AsChangeset, Debug, Serialize, Deserialize)]
#[diesel(table_name = crate::schema::users)]
pub struct UpdateUser<'a> {
pub username: Option<&'a str>,
pub email: Option<&'a str>,
pub active: Option<bool>,
}
データベース接続プール
src/db.rs ファイルを作成します:
use diesel::mysql::MysqlConnection;
use diesel::r2d2::{self, ConnectionManager};
use std::env;
pub type Pool = r2d2::Pool<ConnectionManager<MysqlConnection>>;
pub fn init_pool() -> Pool {
let database_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set");
let manager = ConnectionManager::<MysqlConnection>::new(database_url);
r2d2::Pool::builder()
.build(manager)
.expect("Failed to create pool")
}
src/lib.rs 内でデータベースモジュールをエクスポートします:
pub mod models;
pub mod schema;
pub mod db;
// よく使われる型の再エクスポート
pub use db::{init_pool, Pool};
pub use models::{User, NewUser, UpdateUser};
データアクセス層の実装
src/repositories/user_repository.rs ファイルを作成します:
use diesel::prelude::*;
use diesel::result::Error;
use crate::models::{User, NewUser, UpdateUser};
use crate::schema::users;
pub struct UserRepository;
impl UserRepository {
pub fn create(conn: &mut MysqlConnection, new_user: NewUser) -> Result<User, Error> {
diesel::insert_into(users::table)
.values(&new_user)
.execute(conn)?;
users::table
.order(users::id.desc())
.first(conn)
}
pub fn find(conn: &mut MysqlConnection, user_id: i32) -> Result<User, Error> {
users::table.find(user_id).first(conn)
}
pub fn update(conn: &mut MysqlConnection, user_id: i32, update_user: UpdateUser) -> Result<User, Error> {
diesel::update(users::table.find(user_id))
.set(&update_user)
.execute(conn)?;
Self::find(conn, user_id)
}
pub fn delete(conn: &mut MysqlConnection, user_id: i32) -> Result<usize, Error> {
diesel::delete(users::table.find(user_id))
.execute(conn)
}
pub fn find_all(conn: &mut MysqlConnection) -> Result<Vec<User>, Error> {
users::table.load(conn)
}
}
モジュールファイルの作成
src/repositories/mod.rsファイルを作成します:pub mod user_repository;src/main.rsファイルを更新します:mod models; mod schema; mod db; mod repositories; use dotenv::dotenv; use log::info; use crate::models::{NewUser, UpdateUser}; use crate::repositories::user_repository::UserRepository; use crate::db::{init_pool}; fn main() -> Result<(), Box<dyn std::error::Error>> { dotenv().ok(); pretty_env_logger::init(); let pool = init_pool(); let mut conn = pool.get()?; // ユーザーの作成 let new_user = NewUser { username: "testuser", email: "test@example.com", active: true, }; let created_user = UserRepository::create(&mut conn, new_user)?; info!("Created user: {:?}", created_user); // ユーザーの検索 let user = UserRepository::find(&mut conn, created_user.id)?; info!("Found user: {:?}", user); // ユーザーの更新 let update_data = UpdateUser { username: Some("updated_user"), email: Some("updated@example.com"), active: Some(false), }; let updated_user = UserRepository::update(&mut conn, user.id, update_data)?; info!("Updated user: {:?}", updated_user); // すべてのユーザーの検索 let all_users = UserRepository::find_all(&mut conn)?; info!("All users: {:?}", all_users); // ユーザーの削除 let count = UserRepository::delete(&mut conn, updated_user.id)?; info!("Deleted {} user(s)", count); Ok(()) }
プログラムの実行
OceanBaseデータベースが起動して実行されていることを確認します。
.envファイルにデータベース接続情報が正しく設定されていることを確認します。プログラムを実行します:
RUST_LOG=info cargo run
コードの説明
プロジェクト構造
db.rs: データベース接続プールの設定models/: データモデルの定義repositories/: データアクセス層migrations/: データベースマイグレーションファイルschema.rs: 自動生成されたデータベーススキーマ
主要なコンポーネント
r2d2を使用してデータベース接続プールを管理するdieselを使用して型安全なクエリビルダを提供するdotenvを使用して環境変数を管理するlogとpretty_env_loggerを使用してログを記録する
エラー処理
- Rust の
Result型を使用したエラー処理 - 詳細なエラー情報とログの提供
?演算子を使用したエラー伝播の簡略化
- Rust の
ベストプラクティス
- データアクセスロジックとビジネスロジックを分離する
- トランザクションを使用してデータの一貫性を保証する
- 適切なエラー処理とログ記録を実装する
- 環境変数を使用して機密情報を管理する