SQLDelight para Kotlin: guía completa con ejemplos prácticos
1. ¿Qué es SQLDelight y por qué usarlo?
SQLDelight es una biblioteca para Kotlin que genera código Kotlin type-safe (tipado seguro) a partir de consultas SQL. Fue desarrollada por Cash App (Square) y es compatible con Kotlin Multiplatform (KMP), lo que permite usar el mismo código para trabajar con bases de datos en Android, iOS, JVM, JS y plataformas nativas.
La idea principal de SQLDelight es que escribes consultas SQL en archivos .sq y la biblioteca genera automáticamente clases, interfaces y funciones Kotlin que coinciden exactamente con tus consultas. Esto elimina la necesidad de escribir código repetitivo para mapear resultados de consultas a objetos y previene inyecciones SQL en tiempo de compilación.
¿Por qué usar SQLDelight? Primero, garantiza una verificación completa de tipos en compilación: si cambias la estructura de una tabla, el compilador te señalará inmediatamente todos los lugares donde debes actualizar las consultas. Segundo, soporta migraciones, transacciones, consultas asíncronas y trabajo multiplataforma. Tercero, se integra con frameworks populares como Ktor, Koin, Dagger/Hilt y otros.
2. Instalación
SQLDelight se instala a través del sistema de compilación Gradle. Para Kotlin Multiplatform, agrega el siguiente plugin en el archivo build.gradle.kts raíz:
// build.gradle.kts raíz
plugins {
id("app.cash.sqldelight") version "2.0.2" apply false
}
// En el módulo (por ejemplo, :shared)
plugins {
id("app.cash.sqldelight")
}
sqldelight {
databases {
create("MyDatabase") {
packageName.set("com.example.db")
}
}
}
dependencies {
implementation("app.cash.sqldelight:coroutines-extensions:2.0.2")
implementation("app.cash.sqldelight:runtime:2.0.2")
// Para Android
implementation("app.cash.sqldelight:android-driver:2.0.2")
// Para iOS
implementation("app.cash.sqldelight:native-driver:2.0.2")
// Para JVM
implementation("app.cash.sqldelight:sqlite-driver:2.0.2")
}
Después de sincronizar Gradle, crea el archivo src/commonMain/sqldelight/com/example/db/MyDatabase.sq (la ruta debe coincidir con packageName).
3. Inicio rápido — ejemplo mínimo funcional
Crea el archivo MyDatabase.sq:
-- MyDatabase.sq
CREATE TABLE Person (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
age INTEGER NOT NULL
);
selectAll:
SELECT * FROM Person;
insertPerson:
INSERT INTO Person(name, age) VALUES (?, ?);
Ahora en código Kotlin:
import com.example.db.MyDatabase
import app.cash.sqldelight.db.SqlDriver
import app.cash.sqldelight.driver.jdbc.sqlite.JdbcSqliteDriver
fun main() {
val driver: SqlDriver = JdbcSqliteDriver("jdbc:sqlite:test.db")
MyDatabase.Schema.create(driver)
val database = MyDatabase(driver)
val queries = database.myDatabaseQueries
// Inserción
queries.insertPerson("Ana", 30)
queries.insertPerson("Carlos", 25)
// Consulta
val people = queries.selectAll().executeAsList()
people.forEach { person ->
println("${person.name} tiene ${person.age} años")
}
}
Este código crea la tabla, inserta dos registros y los muestra.
4. Métodos/funciones/clases principales
4.1 executeAsList()
Firma: fun <T> executeAsList(): List<T>
Qué hace: Ejecuta una consulta SELECT y devuelve todas las filas del resultado como una lista de objetos del tipo generado.
val todasLasPersonas: List<Person> = queries.selectAll().executeAsList()
4.2 executeAsOne()
Firma: fun <T> executeAsOne(): T
Qué hace: Ejecuta una consulta SELECT que debe devolver exactamente una fila. Lanza una excepción si no hay filas o hay más de una.
val persona: Person = queries.selectById(1).executeAsOne()
4.3 executeAsOneOrNull()
Firma: fun <T> executeAsOneOrNull(): T?
Qué hace: Similar a executeAsOne(), pero devuelve null si no hay filas en lugar de lanzar una excepción.
val persona: Person? = queries.selectById(99).executeAsOneOrNull()
4.4 executeAsCount()
Firma: fun executeAsCount(): Long
Qué hace: Ejecuta una consulta SELECT COUNT(*) y devuelve el número de filas como un valor Long.
val total: Long = queries.countAll().executeAsCount()
4.5 execute()
Firma: fun execute(): Int
Qué hace: Ejecuta una consulta de modificación (INSERT, UPDATE, DELETE) y devuelve el número de filas afectadas.
val filasAfectadas: Int = queries.deletePerson(1).execute()
4.6 asFlow() / .asFlow().mapToList()
Firma: fun <T> asFlow(): Flow<QueryResult<T>>
Qué hace: Convierte una consulta en un Flow de Kotlin, permitiendo observar cambios en los datos de forma reactiva. Ideal para arquitecturas modernas con StateFlow o LiveData.
val personasFlow: Flow<List<Person>> = queries.selectAll()
.asFlow()
.mapToList()
// En un ViewModel o corrutina
personasFlow.collect { lista ->
println("Personas actualizadas: $lista")
}
5. Conclusión
SQLDelight es una herramienta poderosa para cualquier desarrollador Kotlin que trabaje con bases de datos. Su enfoque type-safe, soporte multiplataforma y generación automática de código la convierten en una opción ideal para proyectos modernos. Con esta guía tienes todo lo necesario para empezar: desde la instalación hasta los métodos más importantes con ejemplos prácticos.
¿Quieres profundizar más? Explora la documentación oficial de SQLDelight para descubrir características avanzadas como migraciones, transacciones y personalización de drivers.
También en la biblioteca
Validación de datos en TypeScript: Zod, Yup y otras herramientas
Parseo de HTML en PHP: DOMDocument, SimpleXML y cURL para web scraping
Diesel para Rust: guía completa con ejemplos
Validación de datos en Kotlin: bibliotecas y enfoques
Generación de informes PDF en PHP: revisión de bibliotecas y ejemplos de código