Как предварительно заполнить базу данных Room

Если вы хотите, чтобы приложение запускалось с базой данных, в которую уже загружен определенный набор данных, вы можете предварительно заполнить ее. В Room можно использовать API, чтобы при инициализации предварительно заполнить базу данных контентом из предварительно упакованного файла базы данных в файловой системе устройства.

Как заполнить поля на основе объекта приложения

Чтобы предварительно заполнить базу данных Room из предварительно упакованного файла базы данных, который находится в любом месте каталога assets/ вашего приложения, вызовите функцию createFromAsset из объекта RoomDatabase.Builder перед вызовом build:

Room.databaseBuilder<AppDatabase>(appContext, "sample.db")
    .createFromAsset("database/myapp.db")
    .build()

Функция createFromAsset принимает строковый аргумент, содержащий относительный путь от каталога assets/ к предварительно упакованному файлу базы данных.

Автоматическое заполнение из файловой системы

Чтобы предварительно заполнить базу данных Room из файла, который находится в файловой системе устройства за исключением каталога assets/ приложения, вызовите функцию createFromFile из объекта RoomDatabase.Builder перед вызовом build:

Room.databaseBuilder<AppDatabase>(appContext, "sample.db")
    .createFromFile(File("mypath"))
    .build()

Функция createFromFile принимает аргумент File для предварительно упакованного файла базы данных. Room создает копию выбранного файла, а не открывает его напрямую, поэтому убедитесь, что у вашего приложения есть разрешение на чтение файла.

Как выполнять перенос с использованием готовых баз данных

Предварительно упакованные файлы базы данных также могут изменить способ обработки резервных переносов в базе данных Room. Обычно, когда включены деструктивные переносы и Room должен выполнить перенос без пути переноса, Room удаляет все таблицы в базе данных и создает пустую базу данных с указанной схемой для целевой версии. Однако если вы добавите в приложение файл базы данных с тем же номером, что и у целевой версии, Room заполнит воссозданную базу данных содержимым этого файла после выполнения деструктивной миграции.

Подробнее о переносе базы данных Room…

В следующих разделах приведены примеры того, как это работает на практике.

Пример: резервный перенос с помощью предварительно упакованной базы данных

Предположим следующее:

  • В вашем приложении определена база данных Room версии 3.
  • На устройстве уже установлена база данных версии 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()
}

В этом случае:

  1. Поскольку база данных, определенная в вашем приложении, имеет версию 3, а экземпляр базы данных, уже установленный на устройстве, имеет версию 2, необходима миграция.
  2. Поскольку план перехода с версии 2 на версию 3 не реализован, выполняется резервный перенос.
  3. Поскольку вы вызываете функцию fallbackToDestructiveMigration, резервный перенос является деструктивным. Room удаляет экземпляр базы данных, установленный на устройстве.
  4. Поскольку в приложении есть готовый файл базы данных версии 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()
}

В этом случае:

  1. Поскольку база данных, определенная в вашем приложении, имеет версию 3, а база данных, уже установленная на устройстве, – версию 2, необходимо выполнить перенос.
  2. Поскольку существует реализованный путь переноса с версии 2 на версию 3, Room выполняет определенную функцию migrate, чтобы обновить экземпляр базы данных на устройстве до версии 3, сохранив данные, которые уже есть в базе данных. Room не использует предварительно упакованный файл базы данных, поскольку делает это только в случае резервного копирования.

Пример многоэтапной миграции с использованием готовой базы данных

Предварительно упакованные файлы баз данных также могут влиять на перенос, состоящий из нескольких этапов. Рассмотрим следующий случай:

  • В вашем приложении определена база данных Room версии 4.
  • На устройстве уже установлена база данных версии 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()
}

В этом случае:

  1. Поскольку база данных, определенная в вашем приложении, имеет версию 4, а экземпляр базы данных, уже установленный на устройстве, имеет версию 2, необходима миграция.
  2. Поскольку переход с версии 2 на версию 3 не предусмотрен, выполняется резервный перенос.
  3. Поскольку вы вызываете функцию fallbackToDestructiveMigration, резервный перенос является деструктивным. Room удаляет экземпляр базы данных на устройстве.
  4. Поскольку в приложении есть готовый файл базы данных версии 3, Room воссоздает базу данных и заполняет ее содержимым этого файла.
  5. База данных, установленная на устройстве, теперь имеет версию 3. Поскольку она по-прежнему ниже версии, указанной в приложении, потребуется ещё одна миграция.
  6. Поскольку существует реализованный путь переноса с версии 3 на версию 4, Room выполняет определенную функцию migrate, чтобы обновить экземпляр базы данных на устройстве до версии 4, сохранив данные, скопированные из предварительно упакованного файла базы данных версии 3.