إعداد SQLite لـ KMP

تحتوي مكتبة androidx.sqlite على واجهات مجرّدة بالإضافة إلى عمليات تنفيذ أساسية يمكن استخدامها لإنشاء مكتباتك الخاصة التي تصل إلى SQLite. ننصحك باستخدام مكتبة Room التي توفّر طبقة تجريد فوق SQLite للسماح بالوصول إلى قاعدة بيانات أكثر فعالية مع الاستفادة من الإمكانات الكاملة لـ SQLite.

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

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

[versions]
sqlite = "2.7.0"

[libraries]
# The SQLite Driver interfaces
androidx-sqlite = { module = "androidx.sqlite:sqlite", version.ref = "sqlite" }

# The bundled SQLite driver implementation
androidx-sqlite-bundled = { module = "androidx.sqlite:sqlite-bundled", version.ref = "sqlite" }

[plugins]
ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }

واجهات برمجة تطبيقات برنامج تشغيل SQLite

توفر مجموعات مكتبة androidx.sqlite واجهات برمجة تطبيقات منخفضة المستوى للتواصل مع مكتبة SQLite، سواء كانت مضمّنة في المكتبة عند استخدام androidx.sqlite:sqlite-bundled أو في النظام الأساسي المضيف، مثل Android أو iOS عند استخدام androidx.sqlite:sqlite-framework. تتّبع واجهات برمجة التطبيقات عن كثب الوظائف الأساسية لواجهة برمجة التطبيقات SQLite C.

هناك ثلاث واجهات رئيسية:

  • SQLiteDriver: هي نقطة الدخول إلى SQLite، وتُستخدم لفتح اتصالات قاعدة البيانات.
  • SQLiteConnection: تمثّل عنصر sqlite3.
  • SQLiteStatement: تمثّل عنصر sqlite3_stmt.

يعرض المثال التالي واجهات برمجة التطبيقات الأساسية:

fun main() {
  val databaseConnection = BundledSQLiteDriver().open("todos.db")
  databaseConnection.execSQL(
    "CREATE TABLE IF NOT EXISTS Todo (id INTEGER PRIMARY KEY, content TEXT)"
  )
  databaseConnection.prepare(
    "INSERT OR IGNORE INTO Todo (id, content) VALUES (? ,?)"
  ).use { stmt ->
    stmt.bindInt(index = 1, value = 1)
    stmt.bindText(index = 2, value = "Try Room in the KMP project.")
    stmt.step()
  }
  databaseConnection.prepare("SELECT content FROM Todo").use { stmt ->
    while (stmt.step()) {
      println("Action item: ${stmt.getText(0)}")
    }
  }
  databaseConnection.close()
}

على غرار واجهات برمجة التطبيقات SQLite C، يتألف نمط الاستخدام الشائع من الخطوات التالية:

  1. افتح اتصالاً بقاعدة بيانات باستخدام عملية تنفيذ SQLiteDriver التي تم إنشاء مثيل لها.
  2. أعِدّ بيان SQL باستخدام SQLiteConnection.prepare.
  3. نفِّذ SQLiteStatement من خلال ما يلي:
    1. اختياري: اربط الوسيطات باستخدام الدوال bind*.
    2. كرِّر مجموعة النتائج باستخدام الدالة step.
    3. اقرأ الأعمدة من مجموعة النتائج باستخدام الدوال get*.

عمليات تنفيذ برنامج التشغيل

يلخّص الجدول التالي عمليات تنفيذ برنامج التشغيل المتاحة:

اسم الفئة

العناصر

الأنظمة الأساسية المتوافقة

AndroidSQLiteDriver androidx.sqlite:sqlite-framework

Android

NativeSQLiteDriver androidx.sqlite:sqlite-framework

‫iOS وMac وLinux

BundledSQLiteDriver androidx.sqlite:sqlite-bundled

‫Android وiOS وMac وLinux وJVM (أجهزة الكمبيوتر)

WebWorkerSQLiteDriver androidx.sqlite:sqlite-web

‫JavaScript وWebAssembly (WasmJS)

عملية التنفيذ المقترَحة التي يجب استخدامها هي BundledSQLiteDriver المتوفّرة في androidx.sqlite:sqlite-bundled. تتضمّن هذه العملية مكتبة SQLite التي تم تجميعها من المصدر، ما يوفّر أحدث إصدار وتناسقًا على جميع منصات KMP المتوافقة.

برنامج تشغيل SQLite وRoom

تكون واجهات برمجة تطبيقات برنامج التشغيل مفيدة للتفاعلات المنخفضة المستوى مع قاعدة بيانات SQLite. للحصول على مكتبة غنية بالميزات توفّر وصولاً أكثر فعالية إلى SQLite، ننصحك باستخدام Room.

يعتمد RoomDatabase على SQLiteDriver لتنفيذ عمليات قاعدة البيانات، ويجب ضبط عملية تنفيذ باستخدام RoomDatabase.Builder.setDriver. توفر Room كلاً من RoomDatabase.useReaderConnection و RoomDatabase.useWriterConnection للوصول بشكل أكثر مباشرةً إلى اتصالات قاعدة البيانات المُدارة.

نقل البيانات إلى Kotlin Multiplatform

يجب نقل أي استخدام لمكوّنات واجهة برمجة التطبيقات Support SQLite منخفضة المستوى، مثل واجهة SupportSQLiteDatabase، إلى مكوّنات برنامج تشغيل SQLite المكافئة.

Kotlin Multiplatform

إجراء معاملة باستخدام SQLiteConnection منخفض المستوى

val connection: SQLiteConnection = ...
connection.execSQL("BEGIN IMMEDIATE TRANSACTION")
try {
  // perform database operations in transaction
  connection.execSQL("END TRANSACTION")
} catch(t: Throwable) {
  connection.execSQL("ROLLBACK TRANSACTION")
}

تنفيذ طلب بحث بدون نتيجة

val connection: SQLiteConnection = ...
connection.execSQL("ALTER TABLE ...")

تنفيذ طلب بحث بنتيجة ولكن بدون وسيطات

val connection: SQLiteConnection = ...
connection.prepare("SELECT * FROM Pet").use { statement ->
  while (statement.step()) {
    // read columns
    statement.getInt(0)
    statement.getText(1)
  }
}

تنفيذ طلب بحث بنتيجة ووسيطات

connection.prepare("SELECT * FROM Pet WHERE id = ?").use { statement ->
  statement.bindInt(1, id)
  if (statement.step()) {
    // row found, read columns
  } else {
    // row not found
  }
}

Android فقط

إجراء معاملة باستخدام SupportSQLiteDatabase

val database: SupportSQLiteDatabase = ...
database.beginTransaction()
try {
  // perform database operations in transaction
  database.setTransactionSuccessful()
} finally {
  database.endTransaction()
}

تنفيذ طلب بحث بدون نتيجة

val database: SupportSQLiteDatabase = ...
database.execSQL("ALTER TABLE ...")

تنفيذ طلب بحث بنتيجة ولكن بدون وسيطات

val database: SupportSQLiteDatabase = ...
database.query("SELECT * FROM Pet").use { cursor ->
  while (cursor.moveToNext()) {
    // read columns
    cursor.getInt(0)
    cursor.getString(1)
  }
}

تنفيذ طلب بحث بنتيجة ووسيطات

database.query("SELECT * FROM Pet WHERE id = ?", id).use { cursor ->
  if (cursor.moveToNext()) {
    // row found, read columns
  } else {
    // row not found
  }
}