SQLite (Kotlin Multiplatform)

Die androidx.sqlite-Bibliothek enthält abstrakte Schnittstellen sowie grundlegende Implementierungen, mit denen Sie eigene Bibliotheken erstellen können, die auf SQLite zugreifen. Sie sollten die Room-Bibliothek in Betracht ziehen, die eine Abstraktionsebene für SQLite bietet, um einen robusteren Datenbankzugriff zu ermöglichen und gleichzeitig die volle Leistungsfähigkeit 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 die Datei build.gradle.kts für Ihr Modul ein:

[versions]
sqlite = "2.5.2"

[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 APIs auf niedriger Ebene 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 orientieren sich eng an den Kernfunktionen der SQLite C-API.

Es gibt drei Hauptschnittstellen:

  • SQLiteDriver: Dies ist der Einstiegspunkt für die Verwendung von SQLite und für das Öffnen von Datenbankverbindungen zuständig.
  • SQLiteConnection: die Darstellung des sqlite3-Objekts.
  • SQLiteStatement: die Darstellung des sqlite3_stmt-Objekts.

Im folgenden Beispiel werden die wichtigsten APIs vorgestellt:

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 wird in der Regel so vorgegangen:

  • Öffnen Sie eine Datenbankverbindung mit der instanziierten SQLiteDriver-Implementierung.
  • SQL-Anweisung mit SQLiteConnection.prepare() vorbereiten
  • So führen Sie eine SQLiteStatement aus:
    1. Optional können Sie Argumente mit den bind*()-Funktionen binden.
    2. Iterieren Sie mit der Funktion step() über das Ergebnis-Set.
    3. Spalten aus dem Ergebnis-Set mit den get*()-Funktionen lesen.

Treiberimplementierungen

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

Kursname

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)

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

SQLite-Treiber und Room

Die Treiber-APIs sind nützlich für Low-Level-Interaktionen mit einer SQLite-Datenbank. Wenn Sie eine funktionsreiche Bibliothek benötigen, die einen robusteren Zugriff auf SQLite bietet, ist Room die richtige Wahl.

Für einen RoomDatabase ist ein SQLiteDriver erforderlich, um Datenbankvorgänge auszuführen. Eine Implementierung muss mit RoomDatabase.Builder.setDriver() konfiguriert werden. Room bietet RoomDatabase.useReaderConnection und RoomDatabase.useWriterConnection für einen direkteren Zugriff auf die verwalteten Datenbankverbindungen.

Zu Kotlin Multiplatform migrieren

Alle Verwendungen von SQLite-Aufrufen auf niedriger Ebene müssen zu den entsprechenden SQLite-Treiber-Aufrufen migriert werden.

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

Eine 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 durchfü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 ...")

Eine Abfrage mit Ergebnis, aber ohne Argumente ausführen

val database: SupportSQLiteDatabase = ...
database.query("SELECT * FROM Pet").use { cursor ->
  while (cusor.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
  }
}