غرفة (Kotlin Multiplatform)

توفّر مكتبة استمرارية الغرفة طبقة تجريد عبر SQLite للسماح بالوصول إلى قاعدة البيانات بشكل أكثر فعالية مع الاستفادة من القوة الكاملة لـ SQLite. تُركز هذه الصفحة على استخدام Room في مشاريع Kotlin Multiplatform (KMP). لمزيد من المعلومات حول استخدام Room، يمكنك الاطّلاع على حفظ البيانات في قاعدة بيانات محلية باستخدام Room أو نماذجنا الرسمية.

إعداد التبعيات

إنّ الإصدار الحالي من Room الذي يدعم KMP هو 2.7.0-alpha01 أو إصدار أحدث.

لإعداد Room في مشروع KMP، أضِف التبعيات للعناصر في ملف build.gradle.kts للوحدة النمطية:

  • androidx.room:room-gradle-plugin - مكوّن Gradle الإضافي لضبط مخططات الغرفة
  • androidx.room:room-compiler - معالج KSP الذي ينشئ الرموز
  • androidx.room:room-runtime - جزء وقت التشغيل في المكتبة
  • androidx.sqlite:sqlite-bundled - (اختيارية) مكتبة SQLite المجمَّعة

بالإضافة إلى ذلك، عليك ضبط برنامج تشغيل SQLite للغرفة. تختلف هذه العوامل بناءً على النظام الأساسي المستهدف. راجِع عمليات تنفيذ برنامج التشغيل للحصول على أوصاف لعمليات تنفيذ برنامج التشغيل المتاحة.

للحصول على معلومات إضافية بشأن الإعداد، يُرجى الاطّلاع على ما يلي:

تحديد فئات قاعدة البيانات

تحتاج إلى إنشاء فئة قاعدة بيانات مضاف إليها تعليقات توضيحية باستخدام @Database إلى جانب DAO والكيانات داخل مجموعة المصدر المشتركة في وحدة KMP المشتركة. سيسمح وضع هذه الفئات في مصادر مشتركة بمشاركتها عبر جميع المنصات المستهدفة.

// shared/src/commonMain/kotlin/Database.kt

@Database(entities = [TodoEntity::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
  abstract fun getDao(): TodoDao
}

@Dao
interface TodoDao {
  @Insert
  suspend fun insert(item: TodoEntity)

  @Query("SELECT count(*) FROM TodoEntity")
  suspend fun count(): Int

  @Query("SELECT * FROM TodoEntity")
  fun getAllAsFlow(): Flow<List<TodoEntity>>
}

@Entity
data class TodoEntity(
  @PrimaryKey(autoGenerate = true) val id: Long = 0,
  val title: String,
  val content: String
)

تجدر الإشارة إلى أنّه يمكنك استخدام البيانات الفعلية / المتوقّعة لإنشاء عمليات تنفيذ للغرف خاصة بالنظام الأساسي. على سبيل المثال، يمكنك إضافة دالة DAO خاصة بنظام التشغيل ومحدّدة في رمز برمجي شائع باستخدام expect ثم تحديد تعريفات actual باستخدام طلبات بحث إضافية في مجموعات مصادر خاصة بالنظام الأساسي.

إنشاء منصة إنشاء قاعدة البيانات

تحتاج إلى تعريف منصة إنشاء قاعدة بيانات لإنشاء مثيل لغرفة على كل منصة. وهذا هو الجزء الوحيد من واجهة برمجة التطبيقات المطلوب أن يكون في مجموعات مصادر خاصة بالنظام الأساسي بسبب الاختلافات في واجهات برمجة التطبيقات لنظام الملفات. على سبيل المثال، في نظام التشغيل Android، يتم عادةً الحصول على الموقع الجغرافي لقاعدة البيانات من خلال واجهة برمجة التطبيقات Context.getDatabasePath()، بينما في نظام التشغيل iOS، يتم الحصول على الموقع الجغرافي لقاعدة البيانات باستخدام NSHomeDirectory.

Android

لإنشاء مثيل قاعدة البيانات، حدِّد سياق مع مسار قاعدة البيانات. لا تحتاج إلى تحديد مصنع قواعد بيانات.

// shared/src/androidMain/kotlin/Database.kt

fun getDatabaseBuilder(ctx: Context): RoomDatabase.Builder<AppDatabase> {
    val appContext = ctx.applicationContext
    val dbFile = appContext.getDatabasePath("my_room.db")
    return Room.databaseBuilder<AppDatabase>(
        context = appContext,
        name = dbFile.absolutePath
    )
}

iOS

لإنشاء مثيل قاعدة البيانات، قدم مصنع قاعدة بيانات مع مسار قاعدة البيانات. مصنع قاعدة البيانات هو دالة lambda تستدعي دالة إضافة تم إنشاؤها، واسمها instantiateImpl مع مستلِم من النوع KClass<T>، حيث يكون T هو نوع الفئة @Database التي تتضمّن تعليقات توضيحية.

// shared/src/iosMain/kotlin/Database.kt

fun getDatabaseBuilder(): RoomDatabase.Builder<AppDatabase> {
    val dbFilePath = NSHomeDirectory() + "/my_room.db"
    return Room.databaseBuilder<AppDatabase>(
        name = dbFilePath,
        factory =  { AppDatabase::class.instantiateImpl() }
    )
}

JVM (جهاز كمبيوتر مكتبي)

لإنشاء مثيل قاعدة البيانات، حدِّد مسار قاعدة البيانات فقط. لا تحتاج إلى توفير مصنع قواعد بيانات.

// shared/src/commonMain/kotlin/Database.kt

fun getDatabaseBuilder(): RoomDatabase.Builder<AppDatabase> {
    val dbFile = File(System.getProperty("java.io.tmpdir"), "my_room.db")
    return Room.databaseBuilder<AppDatabase>(
        name = dbFile.absolutePath,
    )
}

إنشاء مثيل لقاعدة البيانات

بعد الحصول على RoomDatabase.Builder من إحدى الأدوات الإنشائية الخاصة بالنظام الأساسي، يمكنك ضبط باقي قاعدة بيانات الغرفة باستخدام الرمز المشترك، بالإضافة إلى المثيل الفعلي لقاعدة البيانات.

// shared/src/commonMain/kotlin/Database.kt

fun getRoomDatabase(
    builder: RoomDatabase.Builder<AppDatabase>
): AppDatabase {
    return builder
        .addMigrations(MIGRATIONS)
        .fallbackToDestructiveMigrationOnDowngrade()
        .setDriver(BundledSQLiteDriver())
        .setQueryCoroutineContext(Dispatchers.IO)
        .build()
}

اختيار SQLiteDriver

تستخدم مقتطفات الرمز السابقة السمة BundledSQLiteDriver. وهذا هو برنامج التشغيل الموصى به الذي يتضمن SQLite تم جمعها من المصدر، والذي يوفر إصدار SQLite الأكثر اتساقًا وحداثة عبر جميع الأنظمة الأساسية. إذا أردت استخدام SQLite الذي يوفّره نظام التشغيل، يمكنك استخدام واجهة برمجة التطبيقات setDriver API في مجموعات مصادر خاصة بالنظام الأساسي تحدّد برنامج تشغيل خاص بالنظام الأساسي. بالنسبة إلى Android، يمكنك استخدام AndroidSQLiteDriver، بينما على iOS يمكنك استخدام NativeSQLiteDriver. لاستخدام NativeSQLiteDriver، عليك توفير خيار ربط لكي يرتبط تطبيق iOS بشكل ديناميكي مع نظام SQLite.

// shared/build.gradle.kts

kotlin {
    listOf(
        iosX64(),
        iosArm64(),
        iosSimulatorArm64()
    ).forEach { iosTarget ->
        iosTarget.binaries.framework {
            baseName = "TodoApp"
            isStatic = true
            // Required when using NativeSQLiteDriver
            linkerOpts.add("-lsqlite3")
        }
    }
}

الاختلافات

تم تطوير الغرفة في الأصل كمكتبة لنظام التشغيل Android وتم نقلها لاحقًا إلى برنامج KMP مع التركيز على التوافق مع واجهة برمجة التطبيقات. يختلف إصدار KMP من Room نوعًا ما بين الأنظمة الأساسية عن إصدار Android الخاص. يتم سرد هذه الاختلافات ووصفها على النحو التالي.

حظر دوال DAO

عند استخدام Room for KMP، يجب أن تكون جميع دوال DAO المجمَّعة للأنظمة الأساسية التي لا تعمل بنظام التشغيل Android دوال suspend باستثناء أنواع الإرجاع التفاعلية، مثل Flow.

// shared/src/commonMain/kotlin/MultiplatformDao.kt

@Dao
interface MultiplatformDao {
    // ERROR: Blocking function not valid for non-Android targets
    @Query("SELECT * FROM Entity")
    fun blockingQuery(): List<Entity>

    // OK
    @Query("SELECT * FROM Entity")
    suspend fun query(): List<Entity>

    // OK
    @Query("SELECT * FROM Entity")
    fun queryFlow(): Flow<List<Entity>>

    // ERROR: Blocking function not valid for non-Android targets
    @Transaction
    fun blockingTransaction() { // … }

    // OK
    @Transaction
    suspend fun transaction() { // … }
}

تتوفّر في الغرفة مكتبة kotlinx.coroutines غير المتزامنة والغنية بالميزات التي تقدّمها لغة البرمجة Kotlin على أنظمة أساسية متعدّدة. للحصول على أفضل الوظائف، يتم فرض دوال suspend على مجموعات DAO المجمَّعة في مشروع KMP، باستثناء دوال DAO الخاصة بنظام التشغيل Android للحفاظ على التوافق مع الأنظمة القديمة مع قاعدة التعليمات البرمجية الحالية.

الاختلافات في الميزات في نظام KMP

يصف هذا القسم كيف تختلف الميزات بين إصداري KMP ونظام Android الأساسي من Room.

دوال @RawQuery DAO

سيظهر خطأ في الدوال التي تمت إضافة تعليقات توضيحية إليها باستخدام @RawQuery والتي تم جمعها لأنظمة أساسية غير Android. ونعتزم إتاحة استخدام @RawQuery في إصدار مستقبلي من الغرفة.

طلب معاودة الاتصال

إنّ واجهات برمجة التطبيقات التالية لإعداد عمليات معاودة الاتصال بطلبات البحث غير متاحة بشكل مشترك، وبالتالي فهي غير متاحة في الأنظمة الأساسية غير Android.

  • RoomDatabase.Builder.setQueryCallback
  • RoomDatabase.QueryCallback

ونعتزم إتاحة ميزة معاودة الاتصال بطلبات البحث في إصدار مستقبلي من الغرفة.

لا تتوفّر واجهة برمجة التطبيقات لإعداد RoomDatabase باستخدام طلب معاودة الاتصال RoomDatabase.Builder.setQueryCallback وواجهة معاودة الاتصال RoomDatabase.QueryCallback، وبالتالي لا تتوفّر في أنظمة أساسية أخرى غير Android.

الإغلاق التلقائي لقاعدة البيانات

تتوفّر واجهة برمجة التطبيقات RoomDatabase.Builder.setAutoCloseTimeout التي تتيح تفعيل ميزة الإغلاق التلقائي بعد انتهاء المهلة فقط على نظام التشغيل Android ولا يمكن استخدامها على الأنظمة الأساسية الأخرى.

قاعدة بيانات ما قبل الحزمة

إنّ واجهات برمجة التطبيقات التالية لإنشاء RoomDatabase باستخدام قاعدة بيانات حالية (أي قاعدة بيانات مجمّعة مسبقًا) غير متاحة بشكل مشترك، وبالتالي فهي غير متوفرة على أنظمة أساسية أخرى غير Android. واجهات برمجة التطبيقات هذه هي:

  • RoomDatabase.Builder.createFromAsset
  • RoomDatabase.Builder.createFromFile
  • RoomDatabase.Builder.createFromInputStream
  • RoomDatabase.PrepackagedDatabaseCallback

ونعتزم إضافة دعم لقواعد البيانات المجمّعة مسبقًا في إصدار مستقبلي من Room.

إلغاء صلاحية المثيلات المتعددة

تتيح واجهة برمجة التطبيقات إمكانية إبطال المثيلات المتعددة، لا تتوفّر واجهة برمجة التطبيقات RoomDatabase.Builder.enableMultiInstanceInvalidation إلا على Android وغير متاحة على الأنظمة الأساسية المشتركة أو الأخرى.