本記事では、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 run
src/schema.rs ファイルを作成し、以下の内容を記述します:
diesel::table! {
users (id) {
id -> Integer,
username -> Varchar,
email -> Varchar,
active -> Bool,
}
}
モデルの定義
src/models.rs ファイルを作成します:
```rust
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 ファイルを作成します:
```rust
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型を使用したエラー処理 - 詳細なエラー情報とログの提供
?演算子を使用したエラー伝播の簡略化
- ベストプラクティス
- データアクセスロジックとビジネスロジックを分離する
- トランザクションを使用してデータの一貫性を保証する
- 適切なエラー処理とログ記録を実装する
- 環境変数を使用して機密情報を管理する