הגדרת 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 Driver APIs

קבוצות הספרייה androidx.sqlite מציעות ממשקי API ברמה נמוכה לתקשורת עם ספריית SQLite, שנכללת בספרייה כשמשתמשים ב-androidx.sqlite:sqlite-bundled או בפלטפורמת המארח, כמו Android או iOS כשמשתמשים ב-androidx.sqlite:sqlite-framework. ממשקי ה-API דומים מאוד לפונקציונליות המרכזית של SQLite C API.

יש שלושה ממשקים עיקריים:

  • SQLiteDriver: נקודת הכניסה ל-SQLite, שמשמשת לפתיחת חיבורים למסד נתונים.
  • SQLiteConnection: מייצג את אובייקט sqlite3.
  • SQLiteStatement: מייצג את אובייקט sqlite3_stmt.

בדוגמה הבאה מוצגים ממשקי ה-API העיקריים:

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 APIs, דפוס השימוש הנפוץ כולל את השלבים הבאים:

  1. פותחים חיבור למסד נתונים באמצעות ההטמעה של SQLiteDriver שנוצרה.
  2. מכינים הצהרת SQL באמצעות SQLiteConnection.prepare.
  3. מריצים SQLiteStatement באופן הבא:
    1. אופציונלי: קושרים ארגומנטים באמצעות הפונקציות bind*.
    2. מבצעים איטרציה על קבוצת התוצאות באמצעות הפונקציה step.
    3. קוראים עמודות מתוך קבוצת התוצאות באמצעות הפונקציות get*.

הטמעות של Driver

בטבלה הבאה מפורטים סוגי הדרייברים הזמינים:

שם הכיתה

פריט מידע שנוצר בתהליך פיתוח (Artifact)

פלטפורמות נתמכות

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 Driver ו-Room

ממשקי ה-API של מנהלי ההתקנים שימושיים לאינטראקציות ברמה נמוכה עם מסד נתונים של SQLite. אם אתם רוצים להשתמש בספרייה עשירה בתכונות שמספקת גישה חזקה יותר ל-SQLite, מומלץ להשתמש ב-Room.

RoomDatabase מסתמך על SQLiteDriver כדי לבצע פעולות במסד הנתונים, ולכן צריך להגדיר הטמעה באמצעות RoomDatabase.Builder.setDriver. ‫Room מספקת RoomDatabase.useReaderConnection ו-RoomDatabase.useWriterConnection לגישה ישירה יותר לחיבורים מנוהלים למסד הנתונים.

העברה ל-Kotlin Multiplatform

צריך להעביר כל שימוש ברכיבי API של SQLite ברמה נמוכה, כמו הממשק SupportSQLiteDatabase, לרכיבי SQLite Driver מקבילים.

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
  }
}