SQLite für KMP einrichten

Die Bibliothek androidx.sqlite enthält abstrakte Schnittstellen sowie grundlegende Implementierungen, mit denen Sie eigene Bibliotheken erstellen können, die auf SQLite zugreifen. Sie können auch die Room-Bibliothek verwenden, die eine Abstraktionsebene über SQLite bietet, um einen robusteren Datenbankzugriff zu ermöglichen und gleichzeitig die volle Leistung von SQLite zu nutzen.

Abhängigkeiten einrichten

Wenn Sie SQLite in Ihrem KMP-Projekt einrichten möchten, fügen Sie die Abhängigkeiten für die Artefakte in der Datei build.gradle.kts für Ihr Modul hinzu:

[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-Treiber-APIs

Die androidx.sqlite Bibliotheksgruppen bieten Low-Level-APIs für die Kommunikation mit der SQLite-Bibliothek, die entweder in der Bibliothek enthalten ist, wenn androidx.sqlite:sqlite-bundled verwendet wird, oder auf der Hostplattform, z. B. Android oder iOS wenn androidx.sqlite:sqlite-framework verwendet wird. Die APIs folgen eng der Kernfunktionalität der SQLite C API.

Es gibt drei Hauptschnittstellen:

Das folgende Beispiel zeigt die Kern-APIs:

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

Ähnlich wie bei den SQLite C APIs besteht das gängige Nutzungsmuster aus den folgenden Schritten:

  1. Öffnen Sie eine Datenbankverbindung mit der instanziierten SQLiteDriver-Implementierung.
  2. Bereiten Sie eine SQL-Anweisung mit SQLiteConnection.prepare vor.
  3. Führen Sie eine SQLiteStatement aus. Gehen Sie dazu so vor:
    1. Optional: Binden Sie Argumente mit den Funktionen bind*.
    2. Iterieren Sie mit der Funktion step über das Ergebnis-Set.
    3. Lesen Sie Spalten aus dem Ergebnis-Set mit den Funktionen get*.

Treiberimplementierungen

In der folgenden Tabelle sind die verfügbaren Treiberimplementierungen zusammengefasst:

Klassenname

Artefakt

Unterstützte Plattformen

AndroidSQLiteDriver androidx.sqlite:sqlite-framework

Android

NativeSQLiteDriver androidx.sqlite:sqlite-framework

iOS, Mac und Linux

BundledSQLiteDriver androidx.sqlite:sqlite-bundled

Android, iOS, Mac, Linux und JVM (Desktop)

WebWorkerSQLiteDriver androidx.sqlite:sqlite-web

JavaScript und WebAssembly (WasmJS)

Die empfohlene Implementierung ist BundledSQLiteDriver, die in androidx.sqlite:sqlite-bundled verfügbar ist. Sie enthält die aus der Quelle kompilierte SQLite-Bibliothek und bietet die aktuellste Version und Konsistenz auf allen unterstützten KMP-Plattformen.

SQLite-Treiber und Room

Die Treiber-APIs sind nützlich für Low-Level-Interaktionen mit einer SQLite-Datenbank. Für eine funktionsreiche Bibliothek, die einen robusteren Zugriff auf SQLite bietet, empfehlen wir Room.

Eine RoomDatabase verwendet einen SQLiteDriver, um Datenbankvorgänge auszuführen, und Sie müssen eine Implementierung mit RoomDatabase.Builder.setDriver konfigurieren. Room provides RoomDatabase.useReaderConnection and RoomDatabase.useWriterConnection for more direct access to the managed database connections.

Zu Kotlin Multiplatform migrieren

Sie müssen alle Verwendungen von Low-Level-Support-SQLite-API-Komponenten wie der Schnittstelle SupportSQLiteDatabase zu den entsprechenden SQLite-Treiberkomponenten migrieren.

Kotlin Multiplatform

Transaktion mit Low-Level-SQLiteConnection ausführen

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

Abfrage ohne Ergebnis ausführen

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

Abfrage mit Ergebnis, aber ohne Argumente ausführen

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

Abfrage mit Ergebnis und Argumenten ausführen

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

Nur Android

Transaktion mit SupportSQLiteDatabase ausführen

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

Abfrage ohne Ergebnis ausführen

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

Abfrage mit Ergebnis, aber ohne Argumente ausführen

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

Abfrage mit Ergebnis und Argumenten ausführen

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