ما هي SQLx ولماذا تحتاجها؟
SQLx هي مكتبة Rust غير متزامنة ونقية للتعامل مع قواعد البيانات، تتبع نهج "SQL بدون ORM". على عكس حلول ORM مثل Diesel أو SeaORM، لا تخفي SQLx استعلامات SQL خلف طبقات تجريدية، بل تتيح لك كتابة كود SQL نقي مع ضمان أمان كامل للأنواع في وقت الترجمة.
المزايا الرئيسية لـ SQLx:
- أمان الأنواع أثناء الترجمة — تتحقق SQLx من استعلامات SQL الخاصة بك مقابل مخطط قاعدة البيانات مباشرة أثناء بناء المشروع.
- غير متزامنة فورًا — تدعم المكتبة بالكامل async/await، مما يجعلها مثالية لتطبيقات الويب عالية التحميل.
- دعم أنظمة قواعد بيانات متعددة — PostgreSQL، MySQL، SQLite و MSSQL.
- لا سحر خلف الكواليس — تكتب SQL عادي، والمكتبة تساعد فقط في تمرير المعاملات بأمان والحصول على النتائج.
- الترحيلات (Migrations) — نظام مدمج لإدارة ترحيلات مخطط قاعدة البيانات.
SQLx مثالية للمشاريع التي تحتاج تحكمًا كاملاً في الاستعلامات، أداء عالي، وأمان الأنواع، دون الحاجة لتعلم لغات خاصة معقدة (DSL) لأنظمة ORM.
التثبيت
يتم تثبيت SQLx عبر مدير الحزم Cargo. أضف التبعية في ملف Cargo.toml:
[dependencies]
sqlx = { version = "0.7", features = ["runtime-tokio-rustls", "postgres"] }
tokio = { version = "1", features = ["full"] }
anyhow = "1.0"
اختر علامة الميزة المناسبة حسب قاعدة البيانات المستخدمة:
postgres— لـ PostgreSQLmysql— لـ MySQLsqlite— لـ SQLitemssql— لـ Microsoft SQL Server
لبيئة التشغيل، يمكنك اختيار runtime-tokio-rustls (موصى به) أو runtime-async-std-rustls.
البداية السريعة
لننشئ تطبيقًا بسيطًا يتصل بـ PostgreSQL وينفذ استعلامًا:
use sqlx::postgres::PgPoolOptions;
use anyhow::Result;
#[tokio::main]
async fn main() -> Result<()> {
// إنشاء مجموعة اتصالات
let pool = PgPoolOptions::new()
.max_connections(5)
.connect("postgres://user:password@localhost/mydb")
.await?;
// تنفيذ استعلام بسيط
let row: (i64,) = sqlx::query_as("SELECT $1::bigint")
.bind(42_i64)
.fetch_one(&pool)
.await?;
println!("النتيجة: {}", row.0);
Ok(())
}
هذا الكود ينشئ مجموعة من 5 اتصالات بقاعدة البيانات، وينفذ استعلامًا بمعامل، ويعرض النتيجة.
الطرق والدوال الأساسية في SQLx
1. sqlx::query
الوظيفة: إنشاء استعلام SQL جديد مع إمكانية ربط المعاملات. هذه هي اللبنة الأساسية لجميع العمليات.
المعاملات:
sql: &str— نص استعلام SQL مع عناصر نائبة ($1, $2 لـ PostgreSQL؛ ? لـ MySQL/SQLite)
مثال:
use sqlx::query;
let query = sqlx::query("SELECT * FROM users WHERE id = $1 AND active = $2")
.bind(1)
.bind(true);
2. .bind()
الوظيفة: ربط قيمة بمعامل الاستعلام. ترتيب الاستدعاءات يتوافق مع ترتيب العناصر النائبة في SQL.
المعاملات:
value: impl Encode + Type<DB>— أي قيمة يمكن ترميزها لقاعدة البيانات (i32, String, bool, Option، إلخ)
مثال:
let user_id: i32 = 42;
let user_name: &str = "أحمد";
sqlx::query("INSERT INTO users (id, name) VALUES ($1, $2)")
.bind(user_id)
.bind(user_name)
.execute(&pool)
.await?;
3. .fetch_one()
الوظيفة: جلب صف واحد من نتيجة الاستعلام. إذا لم توجد نتائج أو وجد أكثر من صف، سيتم إرجاع خطأ.
المعاملات:
executor: impl Executor— منفذ قاعدة البيانات (مثل pool أو اتصال)
مثال:
let user: (i32, String) = sqlx::query_as("SELECT id, name FROM users WHERE id = $1")
.bind(1)
.fetch_one(&pool)
.await?;
println!("المستخدم: {} - {}", user.0, user.1);