ترحيل قواعد البيانات في Rust: نظرة عامة على أدوات Diesel و SeaORM و sqlx
إدارة تغييرات مخطط قاعدة البيانات هي إحدى المهام الرئيسية في تطوير أي تطبيق ويب. في نظام Rust البيئي، توجد عدة أدوات ناضجة لهذا الغرض. في هذه المقالة، سنستعرض ثلاثة أساليب رئيسية للترحيل: Diesel و SeaORM و sqlx. يقدم كل منها نهجًا فريدًا لإدارة إصدارات مخطط قاعدة البيانات، واختيار الأداة الصحيحة يمكن أن يؤثر بشكل كبير على بنية المشروع.
1. Diesel — ORM كلاسيكي مع تكامل الترحيلات
Diesel هو ORM آمن وقابل للتوسع ومنشئ استعلامات (Query Builder) لـ Rust، وهو متكامل بشكل وثيق مع نظام الترحيلات. ميزته الرئيسية هي التحقق من الأنواع في وقت الترجمة: يتم إنشاء هياكل الجداول بناءً على مخطط قاعدة البيانات، مما يزيل أخطاء عدم تطابق الأنواع.
الترحيلات في Diesel هي ملفات SQL عادية، منظمة في دليل migrations/. يحتوي كل ترحيل على ملفين: up.sql (تطبيق التغييرات) و down.sql (التراجع).
مثال على هيكل الترحيل لإنشاء جدول المستخدمين:
-- migrations/2024-01-15-120000_create_users/up.sqlCREATE TABLE users ( id SERIAL PRIMARY KEY, username VARCHAR(255) NOT NULL UNIQUE, email VARCHAR(255) NOT NULL UNIQUE, created_at TIMESTAMP NOT NULL DEFAULT NOW());
-- migrations/2024-01-15-120000_create_users/down.sqlDROP TABLE IF EXISTS users;لتشغيل الترحيلات، يتم استخدام أداة CLI diesel_cli:
# التثبيتcargo install diesel_cli --no-default-features --features postgres
# تشغيل الترحيلاتdiesel migration run
# التراجع عن آخر ترحيلdiesel migration revert
# إنشاء ترحيل جديدdiesel migration generate create_usersمزايا Diesel:
- التحقق الكامل من الأنواع في مرحلة الترجمة
- هيكل ترحيلات بسيط وواضح (SQL نقي)
- دعم مدمج للتراجع
- أداء ممتاز
العيوب:
- يتطلب تثبيت أداة CLI إضافية
- إعداد معقد للمشاريع التي تحتوي على قواعد بيانات متعددة
- مرونة أقل مقارنة بـ sqlx
2. SeaORM — ORM غير متزامن مع نظام ترحيلات مرن
SeaORM (المبني على sea-query و sqlx) هو ORM غير متزامن يقدم نهجًا أكثر حداثة للترحيلات. على عكس Diesel، فإن الترحيلات في SeaORM تُكتب بلغة Rust وليس SQL، مما يسمح باستخدام كامل قوة اللغة لوصف تغييرات المخطط.
مثال على الترحيل باستخدام SeaORM:
use sea_orm_migration::prelude::*;
#[derive(DeriveMigrationName)]pub struct Migration;
#[async_trait::async_trait]impl MigrationTrait for Migration { async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { manager .create_table( Table::create() .table(Users::Table) .if_not_exists() .col( ColumnDef::new(Users::Id) .integer() .not_null() .auto_increment() .primary_key(), ) .col(ColumnDef::new(Users::Username).string().not_null()) .col(ColumnDef::new(Users::Email).string().not_null()) .col( ColumnDef::new(Users::CreatedAt) .timestamp() .default(Expr::current_timestamp()), ) .to_owned(), )