SQLite (multiplateforme Kotlin)

La bibliothèque androidx.sqlite contient des interfaces abstraites ainsi que des implémentations de base permettant de créer vos propres bibliothèques utilisant SQLite. Vous pouvez envisager d'utiliser la bibliothèque Room, qui fournit une couche d'abstraction sur SQLite, permettant un accès plus robuste à la base de données tout en exploitant toute la puissance de SQLite.

Configurer des dépendances

Pour configurer SQLite dans votre projet KMP, ajoutez les dépendances pour les artefacts dans le fichier build.gradle.kts de votre module :

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

API du pilote SQLite

Les groupes de bibliothèques androidx.sqlite proposent des API de bas niveau pour communiquer avec la bibliothèque SQLite, incluse dans la bibliothèque lors de l'utilisation de androidx.sqlite:sqlite-bundled ou dans la plate-forme hôte, comme Android ou iOS, lors de l'utilisation de androidx.sqlite:sqlite-framework. Les API suivent de près la fonctionnalité principale de l'API C SQLite.

Il existe trois interfaces principales :

  • SQLiteDriver : point d'entrée pour utiliser SQLite, responsable de l'ouverture des connexions à la base de données.
  • SQLiteConnection : représentation de l'objet sqlite3.
  • SQLiteStatement : représentation de l'objet sqlite3_stmt.

L'exemple suivant présente les API principales :

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

Comme pour les API C SQLite, l'utilisation courante consiste à :

  • Ouvrez une connexion à la base de données à l'aide de l'implémentation SQLiteDriver instanciée.
  • Préparer une instruction SQL à l'aide de SQLiteConnection.prepare()
  • Exécutez un SQLiteStatement de la manière suivante :
    1. Vous pouvez éventuellement lier des arguments à l'aide des fonctions bind*().
    2. Parcourez l'ensemble de résultats à l'aide de la fonction step().
    3. Lisez les colonnes du jeu de résultats à l'aide des fonctions get*().

Implémentations de pilotes

Le tableau suivant récapitule les implémentations de pilotes disponibles :

Nom du cours

Artefact

Plates-formes compatibles

AndroidSQLiteDriver androidx.sqlite:sqlite-framework

Android

NativeSQLiteDriver androidx.sqlite:sqlite-framework

iOS, Mac et Linux

BundledSQLiteDriver androidx.sqlite:sqlite-bundled

Android, iOS, Mac, Linux et JVM (ordinateur)

L'implémentation recommandée est BundledSQLiteDriver, disponible dans androidx.sqlite:sqlite-bundled. Elle inclut la bibliothèque SQLite compilée à partir de la source, offrant la version la plus récente et la cohérence sur toutes les plates-formes KMP compatibles.

Pilote SQLite et Room

Les API de pilote sont utiles pour les interactions de bas niveau avec une base de données SQLite. Si vous recherchez une bibliothèque riche en fonctionnalités qui offre un accès plus robuste à SQLite, Room est recommandé.

Un RoomDatabase s'appuie sur un SQLiteDriver pour effectuer des opérations de base de données. Une implémentation doit être configurée à l'aide de RoomDatabase.Builder.setDriver(). Room fournit RoomDatabase.useReaderConnection et RoomDatabase.useWriterConnection pour un accès plus direct aux connexions de base de données gérées.

Migrer vers Kotlin Multiplatform

Toute utilisation d'appels SQLite de bas niveau doit être migrée vers leurs équivalents dans le pilote SQLite.

Multiplateforme Kotlin

Effectuer une transaction à l'aide de SQLiteConnection de bas niveau

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

Exécuter une requête sans résultat

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

Exécuter une requête avec un résultat, mais sans arguments

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

Exécuter une requête avec des résultats et des arguments

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 uniquement

Effectuer une transaction à l'aide de SupportSQLiteDatabase

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

Exécuter une requête sans résultat

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

Exécuter une requête avec un résultat, mais sans arguments

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

Exécuter une requête avec des résultats et des arguments

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