Android NDK开发详解应用数据和文件之预填充 Room 数据库
有时,您可能希望应用启动时数据库中就已经加载了一组特定的数据。这称为预填充数据库。 在 Room 2.2.0 及更高版本中,您可以使用 API 方法在初始化时用设备文件系统中预封装的数据库文件中的内容预填充 Room 数据库。
注意:内存中 Room 数据库不支持使用 createFromAsset() 或 createFromFile() 预填充数据库。
从应用资源预填充
如需从位于应用 assets/ 目录中的任意位置的预封装数据库文件预填充 Room 数据库,请先从 RoomDatabase.Builder 对象调用 createFromAsset() 方法,然后再调用 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();
createFromAsset() 方法接受包含从 assets/ 目录到预封装数据库文件的相对路径的字符串参数。
注意:从某个资源预填充时,Room 会验证数据库,以便确保其架构与预封装数据库的架构相匹配。在创建预封装数据库文件时,您应导出数据库的架构以作为参考。
从文件系统预填充
如需从位于设备文件系统任意位置(应用的 assets/ 目录除外)的预封装数据库文件预填充 Room 数据库,请先从 RoomDatabase.Builder 对象调用 createFromFile() 方法,然后再调用 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();
createFromFile() 方法接受代表预封装数据库文件的 File 参数。Room 会创建指定文件的副本,而不是直接打开它,因此请确保您的应用具有该文件的读取权限。
注意:从文件系统预填充时,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 的新变化(2019 年 Android 开发者峰会)
博客
封装 Room:使用此方法预填充数据库