0Pricing
Learn Rust Coding · درس

تكامل قاعدة البيانات

حفظ البيانات

تكامل قاعدة البيانات درس مجاني في Learn Rust Coding على CoddyKit. هذا هو الدرس 3 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Learn Rust Coding، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Learn Rust Coding 4 دروس في المجموع.

حفظ البيانات

حتى الآن، كانت واجهة API الخاصة بنا تخزّن البيانات في الذاكرة، فتختفي عند إعادة التشغيل. تحتاج الخدمات الحقيقية إلى قاعدة بيانات. في هذا الدرس، نصل واجهة Axum بقاعدة PostgreSQL باستخدام sqlx، وهي مجموعة أدوات SQL غير متزامنة ومفحوصة وقت الترجمة للغة Rust.

ستتعلّم تجمّعات الاتصالات، والاستعلامات، وربط الصفوف بالبنيات، واستخدام التجمع بوصفه حالة مشتركة.

إضافة sqlx

أضف sqlx مع الميزات التي تحتاج إليها: بيئة التشغيل، وTLS، وبرنامج تشغيل قاعدة البيانات. يوضّح المثال التالي إعداد Postgres باستخدام Tokio.

// Cargo.toml
// [dependencies]
// sqlx = { version = "0.7", features = [
//   "runtime-tokio", "tls-rustls", "postgres", "macros"
// ] }

تجمّع الاتصالات

إنشاء اتصال جديد لكل طلب بطيء. يحافظ تجمّع الاتصالات على مجموعة من الاتصالات القابلة لإعادة الاستخدام. تنشئ PgPoolOptions تجمّعًا انطلاقًا من عنوان URL لقاعدة البيانات. كما أن استنساخ التجمّع منخفض التكلفة، لأنه يعتمد داخليًا على عدّ المراجع، مما يجعله مثاليًا بوصفه حالة مشتركة.

use sqlx::postgres::PgPoolOptions;

async fn make_pool(url: &str) -> sqlx::PgPool {
    PgPoolOptions::new()
        .max_connections(5)
        .connect(url)
        .await
        .expect("failed to connect")
}

التجمّع بوصفه حالة التطبيق

مرّر التجمّع إلى Axum باستخدام .with_state(pool). ثم تستقبل المعالجات State(pool): State<PgPool>. وبما أن استنساخ التجمّع منخفض التكلفة، فإن كل طلب يشارك الاتصالات الأساسية نفسها.

use axum::{routing::get, Router};
use sqlx::PgPool;

fn build(pool: PgPool) -> Router {
    Router::new()
        .route("/todos", get(list_todos))
        .with_state(pool)
}

تنفيذ استعلام

تنفّذ الدالة sqlx::query تعليمات SQL الخام. اربط المعاملات باستخدام .bind(value) لتجنب حقن SQL؛ إذ يستخدم Postgres العناصر النائبة $1 و$2. استخدم .execute(&pool) لعمليات الكتابة التي لا تعيد صفوفًا.

use sqlx::PgPool;

async fn insert_todo(pool: &PgPool, title: &str) -> Result<(), sqlx::Error> {
    sqlx::query("INSERT INTO todos (title, done) VALUES ($1, $2)")
        .bind(title)
        .bind(false)
        .execute(pool)
        .await?;
    Ok(())
}

ربط الصفوف بالبنيات

اشتق sqlx::FromRow في النموذج الخاص بك لكي تُربط نتائج الاستعلام به مباشرةً. استخدم query_as::<_, Todo> مع fetch_all للحصول على Vec<Todo>، أو استخدم fetch_one للحصول على صف واحد.

use sqlx::{PgPool, FromRow};

#[derive(FromRow, serde::Serialize)]
struct Todo { id: i32, title: String, done: bool }

async fn all_todos(pool: &PgPool) -> Result<Vec<Todo>, sqlx::Error> {
    let rows = sqlx::query_as::<_, Todo>("SELECT id, title, done FROM todos")
        .fetch_all(pool)
        .await?;
    Ok(rows)
}

معالج يقرأ قاعدة البيانات

اجمع الأجزاء معًا: يستقبل المعالج التجمّع من الحالة، وينفّذ استعلامًا، ويعيد JSON. اربط أخطاء قاعدة البيانات بالحالة 500 لكي يحصل العميل على استجابة واضحة بدلًا من حدوث انهيار.

use axum::{extract::State, Json, http::StatusCode};
use sqlx::PgPool;

async fn list_todos(
    State(pool): State<PgPool>,
) -> Result<Json<Vec<Todo>>, StatusCode> {
    match all_todos(&pool).await {
        Ok(todos) => Ok(Json(todos)),
        Err(_) => Err(StatusCode::INTERNAL_SERVER_ERROR),
    }
}

إعادة الصفوف المُدرجة

يمكن لـ Postgres إعادة الصف الذي أدرجه للتو باستخدام RETURNING. اجمعه مع query_as وfetch_one للحصول على السجل الجديد، بما في ذلك المعرّف الذي تم إنشاؤه، في رحلة ذهاب وإياب واحدة.

use sqlx::PgPool;

async fn create(pool: &PgPool, title: &str) -> Result<Todo, sqlx::Error> {
    let todo = sqlx::query_as::<_, Todo>(
        "INSERT INTO todos (title, done) VALUES ($1, false) \
         RETURNING id, title, done")
        .bind(title)
        .fetch_one(pool)
        .await?;
    Ok(todo)
}

عمليات الترحيل

يجب أن يكون المخطط موجودًا قبل تنفيذ الاستعلامات. يدعم sqlx عمليات الترحيل: وهي ملفات SQL في مجلد migrations/ تُطبَّق بالترتيب. شغّلها عند بدء التشغيل باستخدام sqlx::migrate!() لإعداد قاعدة بيانات جديدة تلقائيًا.

use sqlx::PgPool;

async fn run_migrations(pool: &PgPool) {
    sqlx::migrate!("./migrations")
        .run(pool)
        .await
        .expect("migrations failed");
}
// migrations/0001_init.sql contains the CREATE TABLE statements.

المعاملات

عندما يجب أن تنجح عدة عمليات كتابة معًا، غلّفها داخل معاملة. ابدأ باستخدام pool.begin()، ونفّذ الاستعلامات على مقبض المعاملة، ثم استدعِ commit. وإذا تخلّصت من المعاملة دون تنفيذ الالتزام، فإن sqlx يتراجع عنها تلقائيًا، محافظًا على اتساق البيانات.

use sqlx::PgPool;

async fn transfer(pool: &PgPool) -> Result<(), sqlx::Error> {
    let mut tx = pool.begin().await?;
    sqlx::query("UPDATE accounts SET balance = balance - 10 WHERE id = 1")
        .execute(&mut *tx).await?;
    sqlx::query("UPDATE accounts SET balance = balance + 10 WHERE id = 2")
        .execute(&mut *tx).await?;
    tx.commit().await?;
    Ok(())
}

الإعدادات والأسرار

لا تضع بيانات اعتماد قاعدة البيانات مباشرةً في الشيفرة أبدًا. اقرأ DATABASE_URL من البيئة، وغالبًا ما يُحمَّل من ملف .env باستخدام حزمة dotenvy أثناء التطوير. وفي بيئة الإنتاج، تحقن المنصة هذه القيمة بوصفها متغير بيئة.

use std::env;

async fn connect_from_env() -> sqlx::PgPool {
    let url = env::var("DATABASE_URL")
        .expect("DATABASE_URL must be set");
    make_pool(&url).await
}

اختبار سريع

اختبر فهمك لدمج قاعدة البيانات.

مراجعة

لقد دمجت قاعدة بيانات:

  • استخدم تجمّع اتصالات PgPool وشاركه عبر .with_state.
  • استخدم query/execute للكتابة، وquery_as مع FromRow للقراءة ذات الأنواع المحددة.
  • اربط المعاملات دائمًا لمنع حقن SQL.
  • تجلب RETURNING الصفوف المُدرجة، بينما تجمع المعاملات عمليات الكتابة بصورة ذرّية.
  • شغّل عمليات الترحيل عند بدء التشغيل، واقرأ DATABASE_URL من البيئة.

الأسئلة الشائعة

هل درس «تكامل قاعدة البيانات» مجاني؟

نعم — نص درس «تكامل قاعدة البيانات» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Learn Rust Coding، انتقل إلى CoddyKit PRO. تتضمن دورة Learn Rust Coding 4 دروس في المجموع.

ماذا ستتعلم في «تكامل قاعدة البيانات»؟

حفظ البيانات تتمرن على Learn Rust Coding مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.

هل أحتاج إلى خبرة سابقة لأبدأ Learn Rust Coding؟

لا تُشترط خبرة سابقة. Learn Rust Coding على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 3 من أصل 4.

كم من الوقت يستغرق درس «تكامل قاعدة البيانات»؟

معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.

هل يمكنني كتابة وتشغيل أكواد في درس Learn Rust Coding هذا؟

نعم. كل درس في Learn Rust Coding يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.

جميع الدروس في هذه الدورة

  1. إعداد المشروع
  2. نقاط النهاية والنماذج
  3. تكامل قاعدة البيانات
  4. اختبار API
← العودة إلى Learn Rust Coding