Room. Міграція версій бази даних
У цьому уроці розглянемо, як виконується міграція версій бази даних у Room
Якщо ви працювали зі стандартними механізмами SQLite в Android, то ви знаєте, що коли ви міняєте структуру бази даних, вам необхідно піднімати версію бази даних і писати SQL запити, які виконають оновлення.
У Room все точно так само. Давайте розглянемо приклад.
У нас є Entity клас працівника
@Entity()
public class Employee {
@PrimaryKey
public long id;
public String name;
public int salary;
}
Додамо йому поле birthday
@Entity()
public class Employee {
@PrimaryKey
public long id;
public String name;
public int salary;
public long birthday;
}
Тепер під час запуску програми ми в логах побачимо помилку: java.lang.IllegalStateException: Room cannot verify the data integrity. Схоже, ви змінили схему, але забули оновити номер версії. Ви можете просто виправити це, збільшивши номер версії
Room повідомляє, що ми вирішили змінити структуру бази даних, а номер версії не змінили.
Ок, виправляємося, і піднімаємо версію бази в Database класі.
@Database(entities = {Employee.class}, version = 2)
public abstract class AppDatabase extends RoomDatabase {
public abstract EmployeeDao employeeDao();
}
Запускаємо застосунок і знову помилка: java.lang.IllegalStateException: Необхідна міграція з 1 на 2. Будь ласка, надайте міграцію в білдері або викличте fallbackToDestructiveMigration в білдері, і в цьому випадку Room заново створить усі таблиці.
Цього разу Room каже, що йому потрібна міграція - тобто SQL-запит, який змінить структуру бази даних так, щоб ця структура відповідала Entity класам. Так, Room поки що не вміє самостійно визначати зміни Entity класів і змінювати під них структуру бази даних.
Або, як варіант, ми можемо використовувати fallbackToDestructiveMigration у білдері створення бази:
database = Room.databaseBuilder(this, AppDatabase.class, "database")
.fallbackToDestructiveMigration()
.build();
У цьому випадку міграцію можна не налаштовувати. Але якщо під час запуску застосунку Room побачить, що необхідна міграція, то він просто перестворить базу відповідно до нової структури Entity класів, і всі дані пропадуть. Так собі варіант, особливо з точки зору користувача. Він втратить усі свої дані.
Давайте зробимо міграцію. Ми в Entity клас Employee додали нове поле birthday. Відповідно в таблицю Employee нам також треба додати нове поле birthday.
Створюємо об'єкт Migration:
public static final Migration MIGRATION_1_2 = new Migration(1, 2) {
@Override
public void migrate(final SupportSQLiteDatabase database) {
database.execSQL("ALTER TABLE Employee ADD COLUMN birthday INTEGER DEFAULT 0 NOT NULL");
}
};
У конструкторі вказуємо стару і нову версію бази. Відповідно, під час міграції бази з версії 1 на 2 Room викличе метод migrate цього Migration об'єкта і в таблиці Employee буде створено нове поле.
У якому саме класі створювати об'єкт MIGRATION_1_2 - справа смаку. Як варіант, можна в AppDatabase.
Залишилося передати цей об'єкт у білдер бази.
database = Room.databaseBuilder(this, AppDatabase.class, "database")
.addMigrations(AppDatabase.MIGRATION_1_2)
.allowMainThreadQueries()
.build();
У методі addMigration вказуємо міграцію.
Якщо міграцій кілька, то їх можна перерахувати через кому.
.addMigrations(AppDatabase.MIGRATION_1_2, AppDatabase.MIGRATION_2_3, AppDatabase.MIGRATION_3_4)
Якщо користувач з першої версії бази оновиться на четверту, то всі ці міграції будуть виконані одна за одною.
У наступному уроці розглянемо, як написати тест для міграції.