如果您希望应用启动时数据库中就已经加载了一组特定的数据,可以预填充数据库。在 Room 中,您可以使用 API 在初始化时用设备文件系统中预封装的数据库文件中的内容预填充数据库。
从应用素材资源预填充
如需从位于应用
目录中的任意位置的预封装数据库文件预填充 Room 数据库,请先从 RoomDatabase.Builder 对象调用 createFromAsset
函数,然后再调用 build:assets/
Room.databaseBuilder<AppDatabase>(appContext, "sample.db") .createFromAsset("database/myapp.db") .build()
createFromAsset 函数接受包含从 assets/ 目录到预封装数据库文件的相对路径的字符串参数。
从文件系统预填充
如需从位于设备文件系统任意位置(应用的 assets/ 目录除外)的预封装数据库文件预填充 Room 数据库,请先从 RoomDatabase.Builder 对象调用 createFromFile 函数,然后再调用 build:
Room.databaseBuilder<AppDatabase>(appContext, "sample.db") .createFromFile(File("mypath")) .build()
createFromFile 函数接受代表
预封装数据库文件的 File 参数。Room 会创建指定文件的副本,而不是直接打开它,因此请确保您的应用具有该文件的读取权限。
处理包含预封装数据库的迁移
预封装的数据库文件还可以更改 Room 数据库处理回退迁移的方式。通常,在启用破坏性迁移且 Room 必须在没有迁移路径的情况下执行迁移时,Room 会丢弃数据库中的所有表,并为目标版本创建具有指定架构的空数据库。但是,如果您添加了版本号与目标版本相同的预封装数据库文件,Room 就会在执行破坏性迁移后使用该预封装数据库文件的内容填充刚刚重新创建的数据库。
如需详细了解 Room 数据库的迁移,请参阅 迁移 Room 数据库。
下面几部分提供了几个实际操作的示例。
示例:使用预封装数据库进行回退迁移
假设以下情况:
- 您的应用定义了一个版本为 3 的 Room 数据库。
- 设备上已安装的数据库实例的版本为 2。
- 存在版本为 3 的预封装数据库文件。
- 未实现从版本 2 到版本 3 的迁移路径。
- 已启用破坏性迁移。
// Database class definition declaring version 3. @Database(entities = [SampleEntity::class], version = 3) abstract class FallbackAppDatabase : RoomDatabase() { // ... } fun createFallbackDb(appContext: Context) { Room.databaseBuilder<FallbackAppDatabase>(appContext, "sample.db") .createFromAsset("database/myapp.db") .fallbackToDestructiveMigration() .build() }
在这种情况下,会发生以下情况:
- 由于应用中定义的数据库的版本为 3,而设备上已安装的数据库实例的版本为 2,因此需要进行迁移。
- 由于未实现从版本 2 到版本 3 的迁移方案,因此该迁移属于回退迁移。
- 由于您调用了
fallbackToDestructiveMigration构建器 函数,因此该回退迁移具有破坏性。Room 会丢弃设备上安装的数据库实例。 - 由于存在版本为 3 的预封装数据库文件,因此 Room 会重新创建数据库,并使用预封装数据库文件的内容对其进行填充。如果预封装数据库文件的版本为 2,Room 会确定它与目标版本不匹配,并且不会在回退迁移中使用它。
示例:使用预封装数据库实现迁移
假设您的应用实现了从版本 2 到版本 3 的迁移路径:
// Database class definition declaring version 3. @Database(entities = [SampleEntity::class], version = 3) abstract class ImplementedAppDatabase : RoomDatabase() { // ... } // Migration path definition from version 2 to version 3. val MIGRATION_2_3 = object : Migration(2, 3) { override suspend fun migrate(connection: SQLiteConnection) { // ... } } fun createImplementedDb(appContext: Context) { Room.databaseBuilder<ImplementedAppDatabase>(appContext, "sample.db") .createFromAsset("database/myapp.db") .addMigrations(MIGRATION_2_3) .build() }
在这种情况下,会发生以下情况:
- 由于应用中定义的数据库的版本为 3,而设备上已安装的数据库的版本为 2,因此需要进行迁移。
- 由于已实现从版本 2 到版本 3 的迁移路径,
Room 会运行已定义的
migrate函数将设备上的数据库 实例更新到版本 3,从而保留该数据库内已有的 数据。Room 不使用预封装的数据库文件,因为 Room 仅在回退迁移中才会使用预封装的数据库文件。
示例:使用预封装数据库进行多步迁移
预封装的数据库文件还可能影响包含多个步骤的迁移。考虑以下情况:
- 您的应用定义了一个版本为 4 的 Room 数据库。
- 设备上已安装的数据库实例的版本为 2。
- 存在版本为 3 的预封装数据库文件。
- 已实现从版本 3 到版本 4 的迁移路径,但未实现从版本 2 到版本 3 的迁移路径。
- 已启用破坏性迁移。
// Database class definition declaring version 4. @Database(entities = [SampleEntity::class], version = 4) abstract class MultiStepAppDatabase : RoomDatabase() { // ... } val MIGRATION_3_4 = object : Migration(3, 4) { override suspend fun migrate(connection: SQLiteConnection) { // ... } } fun createMultiStepDb(appContext: Context) { Room.databaseBuilder<MultiStepAppDatabase>(appContext, "sample.db") .createFromAsset("database/myapp.db") .addMigrations(MIGRATION_3_4) .fallbackToDestructiveMigration() .build() }
在这种情况下,会发生以下情况:
- 由于应用中定义的数据库的版本为 4,而设备上已安装的数据库实例的版本为 2,因此需要进行迁移。
- 由于未实现从版本 2 到版本 3 的迁移路径,因此该迁移属于回退迁移。
- 由于您调用了
fallbackToDestructiveMigration构建器 函数,因此该回退迁移具有破坏性。Room 会丢弃设备上的数据库实例。 - 由于存在版本为 3 的预封装数据库文件,因此 Room 会重新创建数据库,并使用预封装数据库文件的内容对其进行填充。
- 现在,设备上安装的数据库的版本为 3。由于它仍低于应用中定义的版本,因此需要进行另一次迁移。
- 由于已实现从版本 3 到版本 4 的迁移路径,
因此 Room 会运行已定义的
migrate函数将设备上的数据库 实例更新到版本 4,从而保留从版本为 3 的已封装数据库文件中复制的数据。