Configura SQLite per KMP

La libreria androidx.sqlite contiene interfacce astratte e implementazioni di base che possono essere utilizzate per creare librerie personalizzate che accedono a SQLite. Potresti prendere in considerazione l'utilizzo della libreria Room, che fornisce un livello di astrazione su SQLite per consentire un accesso al database più affidabile sfruttando al contempo tutta la potenza di SQLite.

Configurare le dipendenze

Per configurare SQLite nel tuo progetto KMP, aggiungi le dipendenze per gli artefatti nel file build.gradle.kts del modulo:

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

API del driver SQLite

I gruppi di librerie androidx.sqlite offrono API di basso livello per comunicare con la libreria SQLite inclusa nella libreria quando si utilizza androidx.sqlite:sqlite-bundled o nella piattaforma host, come Android o iOS quando si utilizza androidx.sqlite:sqlite-framework. Le API seguono da vicino la funzionalità principale dell'API C di SQLite.

Esistono tre interfacce principali:

L'esempio seguente mostra le API principali:

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

Analogamente alle API C di SQLite, il pattern di utilizzo comune è costituito da questi passaggi:

  1. Apri una connessione al database utilizzando l'implementazione SQLiteDriver di cui è stata creata un'istanza.
  2. Prepara un'istruzione SQL utilizzando SQLiteConnection.prepare.
  3. Esegui un SQLiteStatement procedendo nel seguente modo:
    1. (Facoltativo) Associa gli argomenti utilizzando le funzioni bind*.
    2. Esegui l'iterazione sul set di risultati utilizzando la funzione step.
    3. Leggi le colonne dal set di risultati utilizzando le funzioni get*.

Implementazioni del driver

La seguente tabella riassume le implementazioni del driver disponibili:

Nome corso

Artefatto

Piattaforme supportate

AndroidSQLiteDriver androidx.sqlite:sqlite-framework

Android

NativeSQLiteDriver androidx.sqlite:sqlite-framework

iOS, Mac e Linux

BundledSQLiteDriver androidx.sqlite:sqlite-bundled

Android, iOS, Mac, Linux e JVM (desktop)

WebWorkerSQLiteDriver androidx.sqlite:sqlite-web

JavaScript e WebAssembly (WasmJS)

L'implementazione consigliata da utilizzare è BundledSQLiteDriver, disponibile in androidx.sqlite:sqlite-bundled. Include la libreria SQLite compilata dal codice sorgente, offrendo la versione più aggiornata e la coerenza su tutte le piattaforme KMP supportate.

Driver SQLite e Room

Le API del driver sono utili per le interazioni di basso livello con un database SQLite. Per una libreria ricca di funzionalità che fornisce un accesso più affidabile a SQLite, ti consigliamo Room.

Un RoomDatabase si basa su un SQLiteDriver per eseguire le operazioni del database, e devi configurare un'implementazione utilizzando RoomDatabase.Builder.setDriver. Room provides RoomDatabase.useReaderConnection and RoomDatabase.useWriterConnection for more direct access to the managed database connections.

Eseguire la migrazione a multipiattaforma Kotlin

Devi eseguire la migrazione di qualsiasi utilizzo di componenti dell'API SQLite di supporto di basso livello, come l'interfaccia SupportSQLiteDatabase, ai componenti del driver SQLite equivalenti.

Multipiattaforma Kotlin

Esegui una transazione utilizzando SQLiteConnection di basso livello

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

Esegui una query senza risultati

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

Esegui una query con risultati, ma senza argomenti

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

Esegui una query con risultati e argomenti

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

Solo Android

Esegui una transazione utilizzando SupportSQLiteDatabase

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

Esegui una query senza risultati

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

Esegui una query con risultati, ma senza argomenti

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

Esegui una query con risultati e argomenti

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