A veces, es posible que quieras que la app comience con una base de datos que ya esté cargada con un conjunto específico de datos. Esto se llama prepropagar una base de datos. En Room 2.2.0 y versiones posteriores, puedes usar métodos de API para prepropagar contenido de un archivo de base de datos empaquetado previamente en el sistema de archivos del dispositivo en una base de datos de Room.
Cómo prepropagar desde un elemento de la app
Para prepropagar una base de datos de Room a partir de un archivo de base de datos empaquetado previamente que está en cualquier lugar del directorio assets/
de la app, llama al método createFromAsset()
del objeto RoomDatabase.Builder
antes de llamar a build()
:
Kotlin
Room.databaseBuilder(appContext, AppDatabase::class.java, "Sample.db") .createFromAsset("database/myapp.db") .build()
Java
Room.databaseBuilder(appContext, AppDatabase.class, "Sample.db") .createFromAsset("database/myapp.db") .build();
El método createFromAsset()
acepta un argumento de string que contiene una ruta relativa del directorio assets/
al archivo de base de datos empaquetado previamente.
Cómo prepropagar desde el sistema de archivos
Para prepropagar una base de datos de Room a partir de un archivo de base de datos empaquetado previamente que está en cualquier parte del sistema de archivos del dispositivo, excepto el directorio assets/
de la app, llama al método createFromFile()
desde el objeto RoomDatabase.Builder
antes de llamar a build()
:
Kotlin
Room.databaseBuilder(appContext, AppDatabase::class.java, "Sample.db") .createFromFile(File("mypath")) .build()
Java
Room.databaseBuilder(appContext, AppDatabase.class, "Sample.db") .createFromFile(new File("mypath")) .build();
El método createFromFile()
acepta un argumento File
para el archivo de base de datos empaquetado previamente. Room crea una copia del archivo designado en lugar de abrirlo directamente. Por lo tanto, debes asegurarte de que la app tenga permisos de lectura para el archivo.
Cómo controlar migraciones que incluyan bases de datos empaquetadas previamente
Los archivos de base de datos empaquetados previamente también pueden cambiar cómo tu base de datos de Room controla las migraciones de resguardo. Por lo general, cuando las migraciones destructivas están habilitadas y Room debe hacer una migración sin una ruta de migración, descarta todas las tablas de la base de datos y crea una base de datos vacía con el esquema especificado para la versión objetivo. Sin embargo, si incluyes un archivo de base de datos con el mismo número que la versión de destino, Room intenta propagar la base de datos recién creada con el contenido del archivo de base de datos empaquetado previamente después de hacer la migración destructiva.
Obtén más información en Cómo migrar bases de datos de Room.
En las siguientes secciones, se presentan algunos ejemplos de cómo funciona esto en la práctica.
Ejemplo: migración de resguardo con una base de datos empaquetada previamente
Supongamos lo siguiente:
- Tu app define una base de datos de Room en la versión 3.
- La instancia de base de datos ya instalada en el dispositivo está en la versión 2.
- Hay un archivo de base de datos empaquetado previamente que está en la versión 3.
- No se implementó la ruta de migración de la versión 2 a la 3.
- Las migraciones destructivas están habilitadas.
Kotlin
// Database class definition declaring version 3. @Database(version = 3) abstract class AppDatabase : RoomDatabase() { ... } // Destructive migrations are enabled and a prepackaged database // is provided. Room.databaseBuilder(appContext, AppDatabase::class.java, "Sample.db") .createFromAsset("database/myapp.db") .fallbackToDestructiveMigration() .build()
Java
// Database class definition declaring version 3. @Database(version = 3) public abstract class AppDatabase extends RoomDatabase { ... } // Destructive migrations are enabled and a prepackaged database // is provided. Room.databaseBuilder(appContext, AppDatabase.class, "Sample.db") .createFromAsset("database/myapp.db") .fallbackToDestructiveMigration() .build();
Lo que ocurre en esta situación es lo siguiente:
- Como la base de datos definida en la app está en la versión 3 y la instancia de base de datos ya instalada en el dispositivo está en la versión 2, se necesita una migración.
- Como no hay un plan de migración implementado de la versión 2 a la versión 3, la migración es de resguardo.
- Como se llama al método del compilador
fallbackToDestructiveMigration()
, la migración de resguardo es destructiva. Room descarta la instancia de base de datos que está instalada en el dispositivo. - Como hay un archivo de base de datos empaquetado previamente que está en la versión 3, Room vuelve a crear la base de datos con el contenido de ese archivo. Por otro lado, si el archivo de base de datos empaquetado previamente estuviera en la versión 2, Room observaría que no coincide con la versión de destino y no lo usaría como parte de la migración de resguardo.
Ejemplo: Migración implementada con una base de datos empaquetada previamente
Supongamos que tu app implementa una ruta de migración de la versión 2 a la 3:
Kotlin
// Database class definition declaring version 3. @Database(version = 3) abstract class AppDatabase : RoomDatabase() { ... } // Migration path definition from version 2 to version 3. val MIGRATION_2_3 = object : Migration(2, 3) { override fun migrate(database: SupportSQLiteDatabase) { ... } } // A prepackaged database is provided. Room.databaseBuilder(appContext, AppDatabase::class.java, "Sample.db") .createFromAsset("database/myapp.db") .addMigrations(MIGRATION_2_3) .build()
Java
// Database class definition declaring version 3. @Database(version = 3) public abstract class AppDatabase extends RoomDatabase { ... } // Migration path definition from version 2 to version 3. static final Migration MIGRATION_2_3 = new Migration(2, 3) { @Override public void migrate(SupportSQLiteDatabase database) { ... } }; // A prepackaged database is provided. Room.databaseBuilder(appContext, AppDatabase.class, "Sample.db") .createFromAsset("database/myapp.db") .addMigrations(MIGRATION_2_3) .build();
Lo que ocurre en esta situación es lo siguiente:
- Como la base de datos definida en la app está en la versión 3 y la base de datos ya instalada en el dispositivo está en la versión 2, se necesita una migración.
- Como hay una ruta de migración implementada de la versión 2 a la versión 3, Room ejecuta el método
migrate()
definido para actualizar la instancia de base de datos en el dispositivo a la versión 3 y mantiene los datos que ya están en la base de datos. Room no usa el archivo de base de datos empaquetado previamente, ya que solo los usa en el caso de una migración de resguardo.
Ejemplo: Migración de varios pasos con una base de datos empaquetada previamente
Los archivos de base de datos empaquetados previamente también pueden afectar las migraciones que constan de varios pasos. Considera el siguiente caso:
- Tu app define una base de datos de Room en la versión 4.
- La instancia de base de datos ya instalada en el dispositivo está en la versión 2.
- Hay un archivo de base de datos empaquetado previamente que está en la versión 3.
- Hay una ruta de migración implementada de la versión 3 a la versión 4, pero no de la versión 2 a la 3.
- Las migraciones destructivas están habilitadas.
Kotlin
// Database class definition declaring version 4. @Database(version = 4) abstract class AppDatabase : RoomDatabase() { ... } // Migration path definition from version 3 to version 4. val MIGRATION_3_4 = object : Migration(3, 4) { override fun migrate(database: SupportSQLiteDatabase) { ... } } // Destructive migrations are enabled and a prepackaged database is // provided. Room.databaseBuilder(appContext, AppDatabase::class.java, "Sample.db") .createFromAsset("database/myapp.db") .addMigrations(MIGRATION_3_4) .fallbackToDestructiveMigration() .build()
Java
// Database class definition declaring version 4. @Database(version = 4) public abstract class AppDatabase extends RoomDatabase { ... } // Migration path definition from version 3 to version 4. static final Migration MIGRATION_3_4 = new Migration(3, 4) { @Override public void migrate(SupportSQLiteDatabase database) { ... } }; // Destructive migrations are enabled and a prepackaged database is // provided. Room.databaseBuilder(appContext, AppDatabase.class, "Sample.db") .createFromAsset("database/myapp.db") .addMigrations(MIGRATION_3_4) .fallbackToDestructiveMigration() .build();
Lo que ocurre en esta situación es lo siguiente:
- Como la base de datos definida en la app está en la versión 4 y la instancia de base de datos ya instalada en el dispositivo está en la versión 2, se necesita una migración.
- Como no hay una ruta de migración implementada de la versión 2 a la versión 3, la migración es de resguardo.
- Como se llama al método del compilador
fallbackToDestructiveMigration()
, la migración de resguardo es destructiva. Room descarta la instancia de base de datos en el dispositivo. - Como hay un archivo de base de datos empaquetado previamente que está en la versión 3, Room vuelve a crear la base de datos con el contenido de ese archivo.
- La base de datos instalada en el dispositivo ahora está en la versión 3. Como aún es inferior a la versión definida en tu app, se necesita otra migración.
- Como hay una ruta de migración implementada de la versión 3 a la 4, Room ejecuta el método
migrate()
definido para actualizar la instancia de base de datos del dispositivo a la versión 4 y mantiene los datos que se copiaron del archivo de base de datos empaquetado previamente de la versión 3.
Recursos adicionales
Para obtener más información sobre cómo prepropagar una base de datos de Room, consulta los siguientes recursos adicionales.
Videos
- Novedades de Room (Android Dev Summit 2019)