A biblioteca de persistência Room oferece uma camada de abstração sobre o SQLite para permitir um acesso mais robusto ao banco de dados, aproveitando toda a capacidade do SQLite. Esta página se concentra no uso do Room em projetos Kotlin Multiplatform (KMP). Para mais informações sobre como usar o Room, consulte Salvar dados em um banco de dados local usando o Room ou nossos exemplos oficiais (links em inglês).
Como configurar dependências
A versão atual do Room com suporte ao KMP é 2.7.0-alpha01 ou mais recente.
Para configurar o Room no seu projeto KMP, adicione as dependências dos artefatos no
arquivo build.gradle.kts
do seu módulo:
androidx.room:room-gradle-plugin
: o plug-in do Gradle para configurar esquemas do Room.androidx.room:room-compiler
: processador KSP que gera códigoandroidx.room:room-runtime
: parte do ambiente de execução da bibliotecaandroidx.sqlite:sqlite-bundled
: (opcional) a biblioteca SQLite agrupada.
Além disso, você precisa configurar o driver SQLite do Room. Esses drivers diferem com base na plataforma de destino. Consulte Implementações de driver para ver descrições das implementações disponíveis.
Para mais informações sobre configuração, consulte:
- Defina o local do esquema usando o plug-in do Gradle para Room.
- KSP com Kotlin Multiplatform.
- Como adicionar dependências de ambiente de execução.
Como definir as classes do banco de dados
Você precisa criar uma classe de banco de dados anotada com @Database
, além de DAOs e entidades dentro do conjunto de origem comum do módulo KMP compartilhado. Colocar
essas classes em fontes comuns permite que elas sejam compartilhadas em todas as plataformas
de destino.
// shared/src/commonMain/kotlin/Database.kt
@Database(entities = [TodoEntity::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
abstract fun getDao(): TodoDao
}
@Dao
interface TodoDao {
@Insert
suspend fun insert(item: TodoEntity)
@Query("SELECT count(*) FROM TodoEntity")
suspend fun count(): Int
@Query("SELECT * FROM TodoEntity")
fun getAllAsFlow(): Flow<List<TodoEntity>>
}
@Entity
data class TodoEntity(
@PrimaryKey(autoGenerate = true) val id: Long = 0,
val title: String,
val content: String
)
Você pode usar declarações reais / esperadas para criar
implementações do Room específicas da plataforma. Por exemplo, é possível adicionar um
DAO específico da plataforma que é definido no código comum usando expect
e, em seguida,
especificar as definições de actual
com outras consultas em conjuntos de origem
específicos da plataforma.
Como criar o builder de banco de dados
É necessário definir um builder de banco de dados para instanciar o Room em cada plataforma. Essa
é a única parte da API que precisa estar em conjuntos de origem específicos
da plataforma devido às diferenças nas APIs do sistema de arquivos. Por exemplo, no Android,
o local do banco de dados geralmente é recebido pela
API Context.getDatabasePath()
. Já no iOS, ele é
recebido usando NSHomeDirectory
.
Android
Para criar a instância do banco de dados, especifique um contexto com o caminho do banco de dados. Não é preciso especificar uma fábrica de banco de dados.
// shared/src/androidMain/kotlin/Database.kt
fun getDatabaseBuilder(ctx: Context): RoomDatabase.Builder<AppDatabase> {
val appContext = ctx.applicationContext
val dbFile = appContext.getDatabasePath("my_room.db")
return Room.databaseBuilder<AppDatabase>(
context = appContext,
name = dbFile.absolutePath
)
}
iOS
Para criar a instância do banco de dados, forneça uma fábrica de banco de dados com o
caminho do banco de dados. A fábrica do banco de dados é uma função lambda que invoca uma
função de extensão gerada com o nome instantiateImpl
com um receptor do
tipo KClass<T>
, em que T
é o tipo da classe com anotação @Database
.
// shared/src/iosMain/kotlin/Database.kt
fun getDatabaseBuilder(): RoomDatabase.Builder<AppDatabase> {
val dbFilePath = NSHomeDirectory() + "/my_room.db"
return Room.databaseBuilder<AppDatabase>(
name = dbFilePath,
factory = { AppDatabase::class.instantiateImpl() }
)
}
JVM (computador)
Para criar a instância do banco de dados, especifique apenas o caminho do banco de dados. Você não precisa fornecer uma fábrica de banco de dados.
// shared/src/commonMain/kotlin/Database.kt
fun getDatabaseBuilder(): RoomDatabase.Builder<AppDatabase> {
val dbFile = File(System.getProperty("java.io.tmpdir"), "my_room.db")
return Room.databaseBuilder<AppDatabase>(
name = dbFile.absolutePath,
)
}
Instanciação do banco de dados
Depois de extrair o RoomDatabase.Builder
de um dos construtores
específicos da plataforma, é possível configurar o restante do banco de dados do Room em código comum
com a instanciação real do banco de dados.
// shared/src/commonMain/kotlin/Database.kt
fun getRoomDatabase(
builder: RoomDatabase.Builder<AppDatabase>
): AppDatabase {
return builder
.addMigrations(MIGRATIONS)
.fallbackToDestructiveMigrationOnDowngrade()
.setDriver(BundledSQLiteDriver())
.setQueryCoroutineContext(Dispatchers.IO)
.build()
}
Como selecionar um SQLiteDriver
Os snippets de código anteriores usam o BundledSQLiteDriver
. Esse é o
driver recomendado para incluir o SQLite compilado na origem, que oferece a
versão mais consistente e atualizada do SQLite em todas as plataformas. Se você
quiser usar o SQLite fornecido pelo SO, use a API setDriver
em conjuntos de origem
específicos da plataforma que especificam um driver específico. Para Android, você pode usar
AndroidSQLiteDriver
e, para iOS, pode usar NativeSQLiteDriver
. Para
usar o NativeSQLiteDriver
, é necessário fornecer uma opção de vinculador para que o app
iOS seja vinculado dinamicamente ao SQLite do sistema.
// shared/build.gradle.kts
kotlin {
listOf(
iosX64(),
iosArm64(),
iosSimulatorArm64()
).forEach { iosTarget ->
iosTarget.binaries.framework {
baseName = "TodoApp"
isStatic = true
// Required when using NativeSQLiteDriver
linkerOpts.add("-lsqlite3")
}
}
}
Diferenças
O Room foi originalmente desenvolvido como uma biblioteca Android e depois foi migrado para KMP, com foco na compatibilidade com APIs. A versão KMP do Room é um pouco diferente entre as plataformas e a versão específica para Android. Essas diferenças são listadas e descritas da seguinte maneira.
Como bloquear funções DAO
Ao usar o Room para KMP, todas as funções DAO compiladas para plataformas não Android
precisam ser funções suspend
, exceto os tipos de retorno reativos, como
Flow
.
// shared/src/commonMain/kotlin/MultiplatformDao.kt
@Dao
interface MultiplatformDao {
// ERROR: Blocking function not valid for non-Android targets
@Query("SELECT * FROM Entity")
fun blockingQuery(): List<Entity>
// OK
@Query("SELECT * FROM Entity")
suspend fun query(): List<Entity>
// OK
@Query("SELECT * FROM Entity")
fun queryFlow(): Flow<List<Entity>>
// ERROR: Blocking function not valid for non-Android targets
@Transaction
fun blockingTransaction() { // … }
// OK
@Transaction
suspend fun transaction() { // … }
}
O Room se beneficia da biblioteca kotlinx.coroutines
assíncrona e repleta de recursos
que o Kotlin oferece para várias plataformas. Para a funcionalidade ideal, as funções suspend
são aplicadas a DAOs compilados em um projeto KMP, exceto os DAOs específicos do Android para manter a compatibilidade com versões anteriores da base de código
existente.
Diferenças de recursos com o KMP
Esta seção descreve como os recursos diferem entre as versões do KMP e da plataforma Android do Room.
Funções DAO @RawQuery
As funções anotadas com @RawQuery
que são compiladas para plataformas que não são Android
vão produzir um erro. Pretendemos adicionar suporte a @RawQuery
em uma versão
futura do Room.
Callback de consulta
As APIs abaixo para configurar callbacks de consulta não são comuns e, portanto, não estão disponíveis em outras plataformas além do Android.
RoomDatabase.Builder.setQueryCallback
RoomDatabase.QueryCallback
Pretendemos adicionar suporte ao callback de consulta em uma versão futura do Room.
A API para configurar um RoomDatabase
com um callback de consulta
RoomDatabase.Builder.setQueryCallback
e a interface de callback
RoomDatabase.QueryCallback
não estão disponíveis em comum e, portanto, não estão disponíveis
em outras plataformas além do Android.
Banco de dados de fechamento automático
A API para ativar o fechamento automático após um tempo limite,
RoomDatabase.Builder.setAutoCloseTimeout
, está disponível apenas no Android e
não está disponível em outras plataformas.
Banco de dados do pré-pacote
As APIs abaixo para criar um RoomDatabase
usando um banco de dados existente (ou seja, um
banco de dados pré-empacotado) não estão disponíveis em comum e, portanto, não estão disponíveis
em outras plataformas além do Android. São elas:
RoomDatabase.Builder.createFromAsset
RoomDatabase.Builder.createFromFile
RoomDatabase.Builder.createFromInputStream
RoomDatabase.PrepackagedDatabaseCallback
Pretendemos adicionar suporte a bancos de dados pré-empacotados em uma versão futura do Room.
Invalidação de várias instâncias
A API para ativar a invalidação de várias instâncias,
RoomDatabase.Builder.enableMultiInstanceInvalidation
, está disponível apenas no
Android e não está disponível em plataformas comuns ou outras plataformas.