Предположим, у меня есть простая база данных Room:
@Database(entities = {User.class}, version = 1)
abstract class AppDatabase extends RoomDatabase {
public abstract Dao getDao();
}
Теперь я добавляю новую сущность: Pet
и увеличиваю версию до 2:
@Database(entities = {User.class, Pet.class}, version = 2)
abstract class AppDatabase extends RoomDatabase {
public abstract Dao getDao();
}
Конечно, Room выдает исключение: java.lang.IllegalStateException: A migration from 1 to 2 is necessary.
Предполагая, что я не изменил User
класс (поэтому все данные в безопасности), я должен предоставить миграцию, которая просто создает новую таблицу. Итак, я изучаю классы, созданные Room, ищу сгенерированный запрос для создания моей новой таблицы, копирую ее и вставляю в миграцию:
final Migration MIGRATION_1_2 =
new Migration(1, 2) {
@Override
public void migrate(@NonNull final SupportSQLiteDatabase database) {
database.execSQL("CREATE TABLE IF NOT EXISTS `Pet` (`name` TEXT NOT NULL, PRIMARY KEY(`name`))");
}
};
Однако мне неудобно делать это вручную. Есть ли способ сообщить Room: я не касаюсь ни одной из существующих таблиц, поэтому данные в безопасности. Создайте для меня миграцию?
java
android
database-migration
android-room
Петр Александр Хмелёвский
источник
источник
Ответы:
Номер не делает НЕ есть хорошо миграционная система, по крайней мере, пока
2.1.0-alpha03
.Итак, пока у нас не будет более совершенной системы миграции, есть несколько обходных путей для упрощения миграции в комнате.
Поскольку не существует такого метода, как
@Database(createNewTables = true)
илиMigrationSystem.createTable(User::class)
, который должен быть тем или иным, единственно возможный способ - это запуститьCREATE TABLE IF NOT EXISTS `User` (`id` INTEGER, PRIMARY KEY(`id`))
внутри вашего
migrate
метода.val MIGRATION_1_2 = object : Migration(1, 2){ override fun migrate(database: SupportSQLiteDatabase) { database.execSQL("CREATE TABLE IF NOT EXISTS `User` (`id` INTEGER, PRIMARY KEY(`id`))") } }
Чтобы получить вышеуказанный сценарий SQL , у вас есть 4 способа
1. Напишите самостоятельно
По сути, вы должны написать приведенный выше сценарий, который будет соответствовать сценарию, который генерирует Room. Такой способ возможен, а не осуществлен. (Предположим, у вас есть 50 полей)
2. Схема экспорта
Если вы включите
exportSchema = true
в свою@Database
аннотацию, Room создаст схему базы данных в / schemas папки вашего проекта. Использование@Database(entities = [User::class], version = 2, exportSchema = true) abstract class AppDatabase : RoomDatabase { //... }
Убедитесь, что вы включили следующие строки в
build.grade
свой модуль приложенияkapt { arguments { arg("room.schemaLocation", "$projectDir/schemas".toString()) } }
Когда вы запустите или создадите проект, вы получите файл JSON
2.json
, который содержит все запросы в базе данных вашей комнаты."formatVersion": 1, "database": { "version": 2, "identityHash": "325bd539353db508c5248423a1c88c03", "entities": [ { "tableName": "User", "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, PRIMARY KEY(`id`))", "fields": [ { "fieldPath": "id", "columnName": "id", "affinity": "INTEGER", "notNull": true },
Итак, вы можете включить описанный выше метод
createSql
в свойmigrate
метод.3. Получите запрос из AppDatabase_Impl
Если вы не хотите экспортировать схему, вы все равно можете получить запрос, запустив или построив проект, который будет генерировать
AppDatabase_Impl.java
файл. и в пределах указанного файла вы можете иметь.@Override public void createAllTables(SupportSQLiteDatabase _db) { _db.execSQL("CREATE TABLE IF NOT EXISTS `User` (`id` INTEGER, PRIMARY KEY(`id`))");
Внутри
createAllTables
метода будут сценарии создания всех сущностей. Вы можете получить его и включить в свойmigrate
метод.4. Обработка аннотаций.
Как вы могли догадаться, Room генерирует все вышеперечисленное
schema
, а такжеAppDatabase_Impl
файлы во время компиляции и с обработкой аннотаций, которую вы добавляете с помощьюkapt "androidx.room:room-compiler:$room_version"
Это означает, что вы также можете сделать то же самое и создать свою собственную библиотеку обработки аннотаций, которая генерирует все необходимые запросы на создание для вас.
Идея состоит в том, чтобы создать библиотеку обработки аннотаций для аннотаций комнат
@Entity
и@Database
. Возьмем,@Entity
к примеру, класс, помеченный . Вот шаги, которые вам нужно будет выполнитьStringBuilder
и добавьте "СОЗДАТЬ ТАБЛИЦУ, ЕСЛИ НЕ СУЩЕСТВУЕТ"class.simplename
или поtableName
полю@Entity
. Добавьте его в свойStringBuilder
@ColumnInfo
аннотации. Для каждого поля вы должны добавитьid INTEGER NOT NULL
стиль столбца в вашStringBuilder
.@PrimaryKey
ForeignKey
иIndices
если существует.public final class UserSqlUtils { public String createTable = "CREATE TABLE IF NOT EXISTS User (id INTEGER, PRIMARY KEY(id))"; }
Затем вы можете использовать его как
val MIGRATION_1_2 = object : Migration(1, 2){ override fun migrate(database: SupportSQLiteDatabase) { database.execSQL(UserSqlUtils().createTable) } }
Я сделал для себя такую библиотеку, которую вы можете проверить и даже использовать в своем проекте. Обратите внимание, что созданная мной библиотека не заполнена и просто соответствует моим требованиям для создания таблиц.
RoomExtension для лучшей миграции
Приложение, использующее RoomExtension
Надеюсь, это было полезно.
ОБНОВИТЬ
На момент написания этого ответа версия комнаты была,
2.1.0-alpha03
и когда я написал разработчикам по электронной почте, я получил ответК сожалению, у нас все еще отсутствует улучшенная система миграции.
источник
К сожалению, Room не поддерживает автоматическое создание таблиц без потери данных.
Писать миграцию обязательно. В противном случае он сотрет все данные и создаст новую структуру таблицы.
источник
Вы можете сделать так -
@Database(entities = {User.class, Pet.class}, version = 2) abstract class AppDatabase extends RoomDatabase { public abstract Dao getDao(); public abstract Dao getPetDao(); }
Остальное будет таким же, как вы упомянули выше -
db = Room.databaseBuilder(this, AppDatabase::class.java, "your_db") .addMigrations(MIGRATION_1_2).build()
Ссылка - Подробнее
источник
Вы можете добавить следующую команду gradle в свой defaultConfig в своем app.gradle:
javaCompileOptions { annotationProcessorOptions { arguments = ["room.schemaLocation": "$projectDir/schemas".toString()] } }
Когда вы запустите это, он скомпилирует список имен таблиц с соответствующими операторами CREATE TABLE, из которого вы можете просто скопировать и вставить в свои объекты миграции. Возможно, вам придется изменить имена таблиц.
Например, это из моей сгенерированной схемы:
"tableName": "assets", "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`asset_id` INTEGER NOT NULL, `type` INTEGER NOT NULL, `base` TEXT NOT NULL, `name` TEXT NOT NULL, PRIMARY KEY(`asset_id`))"
И поэтому я копирую и вставляю оператор createSql и меняю "$ {TABLE_NAME}" на "assets" имя таблицы и вуаля автоматически сгенерированные операторы создания комнаты.
источник
В этом случае вам не нужно выполнять миграцию, вы можете вызвать .fallbackToDestructiveMigration () при создании экземпляра базы данных.
Пример:
instance = Room.databaseBuilder(context, AppDatabase.class, "database name").fallbackToDestructiveMigration().build();
И не забудьте изменить версию базы данных.
источник
Может быть, в этом случае (если вы только создали новую таблицу, не изменяя другие), вы сможете сделать это, не создавая никаких миграций?
источник