预填充您的 Room 数据库

有时,您可能希望您的应用从已加载特定数据集的数据库开始。这称为预填充数据库。在 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/ 目录到预打包数据库文件的相对路径。

从文件系统预填充

要从位于设备文件系统中应用 assets/ 目录外的任何位置的预打包数据库文件预填充 Room 数据库,请在调用 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();

在这种情况下会发生以下情况:

  1. 因为你的应用中定义的数据库版本为 3,而设备上已安装的数据库实例版本为 2,所以需要进行迁移。
  2. 由于没有实现从版本 2 到版本 3 的迁移方案,因此迁移将是回退迁移。
  3. 由于调用了 fallbackToDestructiveMigration() 构建器方法,回退迁移将是破坏性的。Room 会删除设备上已安装的数据库实例。
  4. 由于存在一个预打包的版本号为 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();

在这种情况下会发生以下情况:

  1. 因为你的应用中定义的数据库版本为 3,而设备上已安装的数据库版本为 2,所以需要进行迁移。
  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();

在这种情况下会发生以下情况:

  1. 因为你的应用中定义的数据库版本为 4,而设备上已安装的数据库实例版本为 2,所以需要进行迁移。
  2. 由于没有实现从版本 2 到版本 3 的迁移路径,因此迁移将是回退迁移。
  3. 由于调用了 fallbackToDestructiveMigration() 构建器方法,回退迁移将是破坏性的。Room 会删除设备上的数据库实例。
  4. 由于存在一个预打包的版本号为 3 的数据库文件,Room 会重新创建数据库,并使用预打包数据库文件的内容填充它。
  5. 现在设备上安装的数据库版本为 3。因为这仍然低于你的应用中定义的版本,所以还需要进行另一次迁移。
  6. 由于存在从版本 3 到版本 4 的迁移路径,Room 会运行定义的 migrate() 方法将设备上的数据库实例更新到版本 4,同时保留从版本 3 预打包数据库文件中复制过来的数据。

其他资源

要了解有关预填充 Room 数据库的更多信息,请参阅以下其他资源。

视频

博客