La biblioteca de persistencias Room brinda una capa de abstracción para SQLite que permite para obtener un acceso más sólido a la base de datos y, al mismo tiempo, aprovechar toda la potencia de SQLite. Esta se centra en el uso de Room en proyectos multiplataforma de Kotlin (KMP). Para ver más Para obtener información sobre el uso de Room, consulta Cómo guardar contenido en una base de datos local con Room. o nuestras muestras oficiales.
Configura dependencias
La versión actual de Room que admite KMP es 2.7.0-alpha01 o una versión posterior
Para configurar Room en tu proyecto de KMP, agrega las dependencias de los artefactos en el
build.gradle.kts
para tu módulo:
androidx.room:room-gradle-plugin
: Es el complemento de Gradle para configurar esquemas de Room.androidx.room:room-compiler
: Es el procesador KSP que genera código.androidx.room:room-runtime
: Es la parte del entorno de ejecución de la biblioteca.androidx.sqlite:sqlite-bundled
: Es la biblioteca SQLite empaquetada (opcional).
Además, debes configurar el controlador SQLite de Room. Estos controladores difieren según la plataforma seleccionada. Consulta Implementaciones de controladores para obtener descripciones de las implementaciones de controladores disponibles.
Para obtener información adicional sobre la configuración, consulta lo siguiente:
- Configura la ubicación del esquema con el complemento de Gradle de Room.
- KSP con Kotlin multiplataforma
- Cómo agregar dependencias del entorno de ejecución
Define las clases de la base de datos
Debes crear una clase de base de datos anotada con @Database
junto con DAOs.
y entidades dentro del conjunto común de orígenes de tu módulo KMP compartido. Ubicación
estas clases en fuentes comunes permitirán que se compartan entre todos los destinos
y plataformas de Google Cloud.
Cuando declaras un objeto expect
con la interfaz
RoomDatabaseConstructor
, el compilador de Room genera el actual
.
de Google Cloud. Android Studio podría emitir una advertencia
"Expected object 'AppDatabaseConstructor' has no actual declaration in
module"
; puedes suprimir la advertencia con @Suppress("NO_ACTUAL_FOR_EXPECT")
.
// shared/src/commonMain/kotlin/Database.kt
@Database(entities = [TodoEntity::class], version = 1)
@ConstructedBy(AppDatabaseConstructor::class)
abstract class AppDatabase : RoomDatabase() {
abstract fun getDao(): TodoDao
}
// The Room compiler generates the `actual` implementations.
@Suppress("NO_ACTUAL_FOR_EXPECT")
expect object AppDatabaseConstructor : RoomDatabaseConstructor<AppDatabase> {
override fun initialize(): AppDatabase
}
@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
)
Ten en cuenta que, de manera opcional, puedes usar declaraciones reales y previstas.
para crear implementaciones de Room específicas de la plataforma. Por ejemplo, puedes agregar una
DAO específico de la plataforma que se define en código común con expect
y, luego,
Especifica las definiciones de actual
con consultas adicionales en recursos específicos de la plataforma
conjuntos de orígenes.
Crea el compilador de bases de datos
Debes definir un compilador de bases de datos para crear una instancia de Room en cada plataforma. Esta
es la única parte de la API que debe estar en una fuente específica de la plataforma
debido a las diferencias
en las APIs de los sistemas de archivos. Por ejemplo, en Android,
la ubicación de la base de datos generalmente se obtiene
Context.getDatabasePath()
, mientras que, en iOS, la ubicación de la base de datos es
obtenerse usando NSFileManager
.
Android
Para crear la instancia de la base de datos, especifica un Contexto junto con la base de datos. ruta de acceso.
// 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 crear la instancia de la base de datos, proporciona una ruta de la base de datos con la
NSFileManager
, generalmente ubicado en NSDocumentDirectory
.
// shared/src/iosMain/kotlin/Database.kt
fun getDatabaseBuilder(): RoomDatabase.Builder<AppDatabase> {
val dbFilePath = documentDirectory() + "/my_room.db"
return Room.databaseBuilder<AppDatabase>(
name = dbFilePath,
)
}
private fun documentDirectory(): String {
val documentDirectory = NSFileManager.defaultManager.URLForDirectory(
directory = NSDocumentDirectory,
inDomain = NSUserDomainMask,
appropriateForURL = null,
create = false,
error = null,
)
return requireNotNull(documentDirectory?.path)
}
JVM (computadoras de escritorio)
Para crear la instancia de la base de datos, proporciona una ruta de la base de datos con Java o Kotlin APIs
// shared/src/jvmMain/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,
)
}
Creación de instancias de la base de datos
Una vez que obtengas el RoomDatabase.Builder
de uno de los canales
constructores, puedes configurar el resto de la base de datos de Room en código común
junto con la creación de instancias reales de la base de datos.
// shared/src/commonMain/kotlin/Database.kt
fun getRoomDatabase(
builder: RoomDatabase.Builder<AppDatabase>
): AppDatabase {
return builder
.addMigrations(MIGRATIONS)
.fallbackToDestructiveMigrationOnDowngrade()
.setDriver(BundledSQLiteDriver())
.setQueryCoroutineContext(Dispatchers.IO)
.build()
}
Cómo seleccionar un SQLiteDriver
Los fragmentos de código anteriores usan BundledSQLiteDriver
. Este es el
controlador recomendado que incluye SQLite compilado desde la fuente, lo que brinda la
más coherente y actualizada de SQLite en todas las plataformas. Si
Si deseas usar el SQLite que proporciona el SO, usa la API de setDriver
en las operaciones
Conjuntos de orígenes que especifican un controlador específico de la plataforma Para Android, puedes usar
AndroidSQLiteDriver
, mientras que para iOS, puedes usar NativeSQLiteDriver
. Para
usa NativeSQLiteDriver
, debes proporcionar una opción de vinculador para que la biblioteca de iOS
se vincula dinámicamente con el sistema SQLite.
// 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")
}
}
}
Diferencias
Room se desarrolló originalmente como una biblioteca de Android y luego se migró a KMP con un enfoque en la compatibilidad de APIs. La versión de KMP de Room difiere un poco entre plataformas y de la versión específica de Android. Estas diferencias son que se enumera y describe de la siguiente manera.
Cómo bloquear funciones DAO
Cuando se usa Room para KMP, todas las funciones DAO se compilan para plataformas que no son de Android
deben ser funciones suspend
, excepto los tipos de datos que se muestran reactivos, como
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() { // … }
}
Room se beneficia de la biblioteca kotlinx.coroutines
asíncrona con muchas funciones
que ofrece Kotlin para varias plataformas. Para obtener una funcionalidad óptima, suspend
se aplican para los DAOs compilados en un proyecto KMP, a excepción de
DAO específicos de Android para mantener la retrocompatibilidad con los DAOs existentes
base de código.
Diferencias entre funciones con KMP
En esta sección, se describe cómo difieren las funciones entre KMP y la plataforma de Android de Room.
Funciones DAO @RawQuery
Funciones anotadas con @RawQuery
que se compilan para plataformas que no son de Android
deberá declarar un parámetro de tipo RoomRawQuery
en lugar de
SupportSQLiteQuery
@Dao
interface TodoDao {
@RawQuery
suspend fun getTodos(query RoomRawQuery): List<TodoEntity>
}
Se puede usar un RoomRawQuery
para crear una consulta en el entorno de ejecución:
suspend fun getTodosWithLowercaseTitle(title: String): List<TodoEntity> {
val query = RoomRawQuery(
sql = "SELECT * FROM TodoEntity WHERE title = ?"
onBindStatement = {
it.bindText(1, title.lowercase())
}
)
return todosDao.getTodos(query)
}
Devolución de llamada de consulta
Las siguientes APIs para configurar devoluciones de llamada de consultas no están disponibles en común y, por lo tanto, no están disponibles en plataformas distintas de Android.
RoomDatabase.Builder.setQueryCallback
RoomDatabase.QueryCallback
Planeamos agregar compatibilidad con la devolución de llamada de consultas en una versión futura de Room.
La API para configurar un RoomDatabase
con una devolución de llamada de consulta
RoomDatabase.Builder.setQueryCallback
junto con la interfaz de devolución de llamada
RoomDatabase.QueryCallback
no están disponibles en común y, por lo tanto, no están disponibles
en otras plataformas distintas de Android.
Cierre de base de datos automático
La API para habilitar el cierre automático después de un tiempo de espera.
RoomDatabase.Builder.setAutoCloseTimeout
, solo está disponible en Android y es
no está disponible en otras plataformas.
Base de datos previa al empaquetado
Las siguientes APIs permiten crear un RoomDatabase
usando una base de datos existente (es decir, una
base de datos empaquetada previamente) no están disponibles en común y, por lo tanto, no están disponibles en
en otras plataformas distintas de Android. Estas son las APIs:
RoomDatabase.Builder.createFromAsset
RoomDatabase.Builder.createFromFile
RoomDatabase.Builder.createFromInputStream
RoomDatabase.PrepackagedDatabaseCallback
Tenemos la intención de agregar compatibilidad con bases de datos empaquetadas previamente en una versión futura de Habitación.
Invalidación de varias instancias
La API para habilitar la invalidación de instancias múltiples
RoomDatabase.Builder.enableMultiInstanceInvalidation
solo está disponible en
Android y no está disponible en las plataformas comunes ni en otras plataformas.