有时,您可能希望应用在启动时就已加载包含特定数据集的数据库。这称为预填充数据库。在 Room 2.2.0 及更高版本中,您可以使用 API 方法在初始化时使用设备文件系统中预打包的数据库文件内容预填充 Room 数据库。
从应用资产中预填充
要从位于应用 assets/
目录中任何位置的预打包数据库文件预填充 Room 数据库,请在调用 build()
之前,从您的 RoomDatabase.Builder
对象调用 createFromAsset()
方法
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();
createFromAsset()
方法接受一个字符串参数,该参数包含从 assets/
目录到预打包数据库文件的相对路径。
从文件系统中预填充
要从设备文件系统中预打包的数据库文件预填充 Room 数据库,该文件位于您的应用 assets/
目录以外的任何位置,请在调用 build()
之前,从您的 RoomDatabase.Builder
对象调用 createFromFile()
方法
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();
createFromFile()
方法接受一个 File
参数,用于指定预打包的数据库文件。Room 会创建指定文件的一个副本,而不是直接打开它,因此请确保您的应用对该文件具有读取权限。
处理包含预打包数据库的迁移
预打包的数据库文件也可以改变您的 Room 数据库处理回退迁移的方式。通常,当启用了破坏性迁移并且 Room 必须在没有迁移路径的情况下执行迁移时,Room 会丢弃数据库中的所有表,并创建一个具有目标版本指定架构的空数据库。但是,如果您包含一个版本号与目标版本号相同的预打包数据库文件,Room 会在执行破坏性迁移后尝试使用预打包数据库文件的内容填充新重新创建的数据库。
有关 Room 数据库迁移的更多信息,请参阅迁移 Room 数据库。
以下部分介绍了一些实际示例。
示例:包含预打包数据库的回退迁移
假设以下情况:
- 您的应用定义了一个版本 3 的 Room 数据库。
- 设备上已安装的数据库实例是版本 2。
- 有一个版本 3 的预打包数据库文件。
- 没有从版本 2 到版本 3 的已实现迁移路径。
- 启用了破坏性迁移。
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();
在这种情况下,会发生以下情况:
- 由于您的应用中定义的数据库是版本 3,而设备上已安装的数据库实例是版本 2,因此需要进行迁移。
- 由于没有从版本 2 到版本 3 的已实现迁移计划,因此迁移是回退迁移。
- 由于调用了
fallbackToDestructiveMigration()
构建器方法,因此回退迁移是破坏性的。Room 会丢弃设备上安装的数据库实例。 - 由于有一个版本 3 的预打包数据库文件,Room 会重新创建数据库并使用预打包数据库文件的内容填充它。另一方面,如果您预打包的数据库文件是版本 2,则 Room 会注意到它与目标版本不匹配,并且不会将其用作回退迁移的一部分。
示例:包含预打包数据库的已实现迁移
假设您的应用实现了从版本 2 到版本 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();
在这种情况下,会发生以下情况:
- 由于您的应用中定义的数据库是版本 3,而设备上已安装的数据库是版本 2,因此需要进行迁移。
- 由于存在从版本 2 到版本 3 的已实现迁移路径,Room 会运行定义的
migrate()
方法,将设备上的数据库实例更新到版本 3,同时保留数据库中已有的数据。Room 不会使用预打包的数据库文件,因为 Room 仅在回退迁移的情况下使用预打包的数据库文件。
示例:包含预打包数据库的多步迁移
预打包的数据库文件也会影响包含多个步骤的迁移。考虑以下情况:
- 您的应用定义了一个版本 4 的 Room 数据库。
- 设备上已安装的数据库实例是版本 2。
- 有一个版本 3 的预打包数据库文件。
- 存在从版本 3 到版本 4 的已实现迁移路径,但没有从版本 2 到版本 3 的迁移路径。
- 启用了破坏性迁移。
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();
在这种情况下,会发生以下情况:
- 由于您的应用中定义的数据库是版本 4,而设备上已安装的数据库实例是版本 2,因此需要进行迁移。
- 由于没有从版本 2 到版本 3 的已实现迁移路径,因此迁移是回退迁移。
- 由于调用了
fallbackToDestructiveMigration()
构建器方法,因此回退迁移是破坏性的。Room 会丢弃设备上的数据库实例。 - 由于有一个版本 3 的预打包数据库文件,Room 会重新创建数据库并使用预打包数据库文件的内容填充它。
- 设备上安装的数据库现在是版本 3。由于这仍然低于您的应用中定义的版本,因此需要再次迁移。
- 由于存在从版本 3 到版本 4 的已实现迁移路径,Room 会运行定义的
migrate()
方法,将设备上的数据库实例更新到版本 4,同时保留从版本 3 预打包数据库文件复制过来的数据。
其他资源
要了解有关预填充 Room 数据库的更多信息,请参阅以下其他资源。
视频
- Room 新特性 (Android Dev Summit '19)