SQLite (Kotlin multiplataforma)

La biblioteca androidx.sqlite contiene interfaces abstractas junto con implementaciones básicas que se pueden usar para compilar tus propias bibliotecas que acceden a SQLite. Te recomendamos que uses la biblioteca de Room, que brinda una capa de abstracción para SQLite que permite acceder a la base de datos sin problemas y, al mismo tiempo, aprovechar toda la potencia de SQLite.

Configura dependencias

Para configurar SQLite en tu proyecto de KMP, agrega las dependencias de los artefactos en el archivo build.gradle.kts de tu módulo:

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

APIs del controlador de SQLite

Los grupos de bibliotecas androidx.sqlite ofrecen APIs de bajo nivel para comunicarse con la biblioteca de SQLite, ya sea incluida en la biblioteca cuando se usa androidx.sqlite:sqlite-bundled o en la plataforma host, como Android o iOS, cuando se usa androidx.sqlite:sqlite-framework. Las APIs siguen de cerca la funcionalidad principal de la API de SQLite en C.

Existen 3 interfaces principales:

  • SQLiteDriver: Es el punto de entrada para usar SQLite y es responsable de abrir conexiones de bases de datos.
  • SQLiteConnection: Es la representación del objeto sqlite3.
  • SQLiteStatement: Es la representación del objeto sqlite3_stmt.

En el siguiente ejemplo, se muestran las APIs 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()
}

Al igual que con las APIs de C de SQLite, el uso común es el siguiente:

  • Abre una conexión de base de datos con la implementación de SQLiteDriver que se creó.
  • Prepara una instrucción de SQL con SQLiteConnection.prepare()
  • Ejecuta un SQLiteStatement de la siguiente manera:
    1. De manera opcional, vincula argumentos con las funciones bind*().
    2. Itera el conjunto de resultados con la función step().
    3. Lee columnas del conjunto de resultados con las funciones get*().

Implementaciones de controladores

En la siguiente tabla, se resumen las implementaciones de controladores disponibles:

Nombre de la clase

Artefacto

Plataformas admitidas

AndroidSQLiteDriver androidx.sqlite:sqlite-framework

Android

NativeSQLiteDriver androidx.sqlite:sqlite-framework

iOS, Mac y Linux

BundledSQLiteDriver androidx.sqlite:sqlite-bundled

Android, iOS, Mac, Linux y JVM (escritorio)

La implementación recomendada para usar es BundledSQLiteDriver, disponible en androidx.sqlite:sqlite-bundled. Incluye la biblioteca de SQLite compilada desde la fuente, lo que ofrece la versión más actualizada y coherencia en todas las plataformas de KMP compatibles.

Controlador de SQLite y Room

Las APIs del controlador son útiles para las interacciones de bajo nivel con una base de datos SQLite. Si buscas una biblioteca con muchas funciones que proporcione un acceso más sólido a SQLite, te recomendamos Room.

Un RoomDatabase depende de un SQLiteDriver para realizar operaciones de bases de datos, y se requiere una implementación para configurarse con RoomDatabase.Builder.setDriver(). Room proporciona RoomDatabase.useReaderConnection y RoomDatabase.useWriterConnection para un acceso más directo a las conexiones de bases de datos administradas.

Migra a Kotlin Multiplatform

Cualquier uso de llamadas de SQLite de bajo nivel debe migrarse a sus equivalentes de SQLite Driver.

Kotlin multiplataforma

Realiza una transacción con SQLiteConnection de bajo nivel

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

Ejecuta una consulta sin resultados

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

Ejecuta una consulta con un resultado, pero sin argumentos

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

Ejecuta una consulta con resultados y argumentos

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 para Android

Cómo realizar una transacción con SupportSQLiteDatabase

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

Ejecuta una consulta sin resultados

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

Ejecuta una consulta con un resultado, pero sin argumentos

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

Ejecuta una consulta con resultados y argumentos

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