SQLDelight para Kotlin: guia completo com exemplos práticos
1. O que é e para que serve
SQLDelight é uma biblioteca para Kotlin que gera código Kotlin type-safe (com segurança de tipos) a partir de consultas SQL. Desenvolvida pela Cash App (Square), ela suporta Kotlin Multiplatform (KMP), permitindo usar o mesmo código para trabalhar com bancos de dados em Android, iOS, JVM, JS e plataformas nativas.
A ideia principal do SQLDelight é que você escreve consultas SQL em arquivos .sq, e a biblioteca gera automaticamente classes, interfaces e funções Kotlin que correspondem exatamente às suas consultas. Isso elimina a necessidade de escrever código boilerplate para mapear resultados de consultas em objetos e previne injeções SQL em tempo de compilação.
Por que usar SQLDelight? Primeiro, ela oferece verificação completa de tipos em tempo de compilação: se você alterar a estrutura de uma tabela, o compilador apontará imediatamente todos os lugares que precisam ser atualizados. Segundo, ela suporta migrações, transações, consultas assíncronas e trabalho multiplataforma. Terceiro, integra-se com frameworks populares como Ktor, Koin, Dagger/Hilt e outros.
2. Instalação
O SQLDelight é instalado através do sistema de build Gradle. Para Kotlin Multiplatform, adicione o seguinte plugin no build.gradle.kts raiz:
// build.gradle.kts raiz
plugins {
id("app.cash.sqldelight") version "2.0.2" apply false
}
// No módulo (ex: :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")
}
Após sincronizar o Gradle, crie o arquivo src/commonMain/sqldelight/com/example/db/MyDatabase.sq (o caminho deve coincidir com o packageName).
3. Início rápido — exemplo mínimo funcional
Crie o arquivo 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 (?, ?);
Agora no 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
// Inserção
queries.insertPerson("Alice", 30)
queries.insertPerson("Bob", 25)
// Consulta
val people = queries.selectAll().executeAsList()
people.forEach { person ->
println("${person.name} tem ${person.age} anos")
}
}
Este código criará a tabela, inserirá dois registros e os exibirá.
4. Principais métodos/funções/classes
4.1 executeAsList()
Assinatura: fun <T> executeAsList(): List<T>
O que faz: Executa uma consulta SELECT e retorna todas as linhas do resultado como uma lista de objetos do tipo gerado.
val allPeople: List<Person> = queries.selectAll().executeAsList()
4.2 executeAsOne()
Assinatura: fun <T> executeAsOne(): T
O que faz: Executa uma consulta SELECT que deve retornar exatamente uma linha. Lança exceção se não houver resultados ou se houver mais de um.
val person: Person = queries.selectById(1).executeAsOne()
4.3 executeAsOneOrNull()
Assinatura: fun <T> executeAsOneOrNull(): T?
O que faz: Similar ao executeAsOne(), mas retorna null se nenhum resultado for encontrado.
val person: Person? = queries.selectById(99).executeAsOneOrNull()
4.4 executeAsCount()
Assinatura: fun executeAsCount(): Long
O que faz: Executa uma consulta que retorna uma contagem (COUNT, SUM, etc.) e retorna o valor como Long.
val total: Long = queries.countPeople().executeAsCount()
4.5 execute()
Assinatura: fun execute(): Int
O que faz: Executa uma instrução de modificação (INSERT, UPDATE, DELETE) e retorna o número de linhas afetadas.
val rowsAffected: Int = queries.deletePerson(1).execute()
4.6 Transações
SQLDelight suporta transações com transaction { }:
database.transaction {
queries.insertPerson("Charlie", 35)
queries.insertPerson("Diana", 28)
// Commit automático se nenhuma exceção for lançada
}
4.7 Consultas assíncronas com Coroutines
Com a extensão coroutines-extensions, você pode usar asFlow() para observar mudanças:
val peopleFlow: Flow<List<Person>> = queries.selectAll()
.asFlow()
.mapToList(Dispatchers.IO)
peopleFlow.collect { people ->
println("Pessoas atualizadas: $people")
}
5. Migrações de banco de dados
SQLDelight gerencia migrações automaticamente. Crie arquivos .sqm na mesma pasta dos arquivos .sq:
-- 1.sqm
CREATE TABLE City (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
population INTEGER NOT NULL
);
A biblioteca aplica as migrações na ordem correta automaticamente.
6. Conclusão
SQLDelight é uma ferramenta poderosa para quem desenvolve com Kotlin, especialmente em projetos multiplataforma. Ela oferece segurança de tipos, reduz código boilerplate e melhora a produtividade. Comece com exemplos simples e explore recursos avançados como migrações, transações e consultas assíncronas conforme sua necessidade.
Também na biblioteca
Serialização de objetos em JavaScript (Node.js): guia completo
Validação de dados em PHP: Guia completo sobre ferramentas e bibliotecas
Biblioteca cpp-httplib: Cliente OAuth 2.0 em C++ para aplicações web