SQLDelight في Kotlin: دليل شامل مع أمثلة تطبيقية
1. ما هي SQLDelight ولماذا تحتاجها؟
SQLDelight هي مكتبة للغة Kotlin تقوم بتوليد كود Kotlin آمن الأنواع (type-safe) من استعلامات SQL. تم تطويرها بواسطة شركة Cash App (Square) وتدعم Kotlin Multiplatform (KMP)، مما يسمح باستخدام نفس الكود للتعامل مع قواعد البيانات على Android و iOS و JVM و JS ومنصات Native.
الفكرة الأساسية لـ SQLDelight هي أنك تكتب استعلامات SQL في ملفات .sq، وتقوم المكتبة تلقائياً بتوليد كلاسات وواجهات ودوال Kotlin التي تتطابق تماماً مع استعلاماتك. هذا يلغي الحاجة لكتابة كود نمطي لتحويل نتائج الاستعلامات إلى كائنات ومعالجة حقن SQL على مستوى الترجمة.
لماذا تستخدم SQLDelight؟ أولاً، توفر فحصاً كاملاً للأنواع في مرحلة الترجمة: إذا غيرت هيكل الجدول، سيشير المترجم فوراً إلى جميع الأماكن التي تحتاج لتحديث الاستعلامات. ثانياً، تدعم الترحيل (migrations)، المعاملات (transactions)، الاستعلامات غير المتزامنة، والعمل مع منصات متعددة. ثالثاً، تتكامل مع الأطر الشهيرة مثل Ktor و Koin و Dagger/Hilt وغيرها.
2. التثبيت
يتم تثبيت SQLDelight عبر نظام بناء Gradle. لـ Kotlin Multiplatform، أضف الإضافة التالية في ملف build.gradle.kts الجذري:
// build.gradle.kts الجذري
plugins {
id("app.cash.sqldelight") version "2.0.2" apply false
}
// في الوحدة (مثلاً :shared)
plugins {
id("app.cash.sqldelight")
}
sqldelight {
databases {
create("MyDatabase") {
packageName.set("com.example.db")
}
}
}
dependencies {
implementation("app.cash.sqldelight:coroutines-extensions:2.0.2")
implementation("app.cash.sqldelight:runtime:2.0.2")
// لنظام Android
implementation("app.cash.sqldelight:android-driver:2.0.2")
// لنظام iOS
implementation("app.cash.sqldelight:native-driver:2.0.2")
// لنظام JVM
implementation("app.cash.sqldelight:sqlite-driver:2.0.2")
}
بعد مزامنة Gradle، أنشئ ملف src/commonMain/sqldelight/com/example/db/MyDatabase.sq (يجب أن يتطابق المسار مع packageName).
3. البداية السريعة — مثال عملي مصغر
أنشئ ملف MyDatabase.sq:
-- MyDatabase.sq
CREATE TABLE Person (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
age INTEGER NOT NULL
);
selectAll:
SELECT * FROM Person;
insertPerson:
INSERT INTO Person(name, age) VALUES (?, ?);
الآن في كود Kotlin:
import com.example.db.MyDatabase
import app.cash.sqldelight.db.SqlDriver
import app.cash.sqldelight.driver.jdbc.sqlite.JdbcSqliteDriver
fun main() {
val driver: SqlDriver = JdbcSqliteDriver("jdbc:sqlite:test.db")
MyDatabase.Schema.create(driver)
val database = MyDatabase(driver)
val queries = database.myDatabaseQueries
// إدراج بيانات
queries.insertPerson("أحمد", 30)
queries.insertPerson("سارة", 25)
// استعلام وجلب
val people = queries.selectAll().executeAsList()
people.forEach { person ->
println("${person.name} عمره ${person.age} سنة")
}
}
هذا الكود سينشئ الجدول، ويدرج سجلين، ثم يعرضهما.
4. أهم الدوال/الكلاسات
4.1 executeAsList()
التوقيع: fun <T> executeAsList(): List<T>
الوظيفة: تنفيذ استعلام SELECT وإرجاع جميع صفوف النتيجة كقائمة من الكائنات من النوع المُولّد.
val allPeople: List<Person> = queries.selectAll().executeAsList()
4.2 executeAsOne()
التوقيع: fun <T> executeAsOne(): T
الوظيفة: تنفيذ استعلام SELECT يتوقع إرجاع صف واحد بالضبط. يرمي استثناء إذا لم يتم العثور على نتائج أو إذا كان هناك أكثر من نتيجة.
val person: Person = queries.selectById(1).executeAsOne()
4.3 executeAsOneOrNull()
التوقيع: fun <T> executeAsOneOrNull(): T?
الوظيفة: مثل executeAsOne() ولكنها تعيد null إذا لم يتم العثور على نتائج.
val person: Person? = queries.selectById(99).executeAsOneOrNull()
4.4 execute()
التوقيع: fun execute(): Long
الوظيفة: تنفيذ استعلام INSERT أو UPDATE أو DELETE. تعيد عدد الصفوف المتأثرة.
val rowsAffected: Long = queries.deletePerson(1).execute()
4.5 executeAsCount()
التوقيع: fun executeAsCount(): Long
الوظيفة: تنفيذ استعلام COUNT وإرجاع القيمة كعدد صحيح طويل.
val count: Long = queries.countPeople().executeAsCount()