В этом уроке рассмотрим, как выполняется миграция версий базы данных в Room
Полный список уроков курса:
- Урок 1. Lifecycle
- Урок 2. LiveData
- Урок 3. LiveData. Дополнительные возможности
- Урок 4. ViewModel
- Урок 5. Room. Основы
- Урок 6. Room. Entity
- Урок 7. Room. Insert, Update, Delete, Transaction
- Урок 8. Room. Query
- Урок 9. Room. RxJava
- Урок 10. Room. Запрос из нескольких таблиц. Relation
- Урок 11. Room. Type converter
- Урок 12. Room. Миграция версий базы данных
- Урок 13. Room. Тестирование
- Урок 14. Paging Library. Основы
- Урок 15. Paging Library. PagedList и DataSource. Placeholders.
- Урок 16. Paging Library. LivePagedListBuilder. BoundaryCallback.
- Урок 17. Paging Library. Виды DataSource
- Урок 18. Android Data Binding. Основы
- Урок 19. Android Data Binding. Код в layout. Доступ к View
- Урок 20. Android Data Binding. Обработка событий
- Урок 21. Android Data Binding. Observable поля. Двусторонний биндинг.
- Урок 22. Android Data Binding. Adapter. Conversion.
- Урок 23. Android Data Binding. Использование с include, ViewStub и RecyclerView.
- Урок 24. Navigation Architecture Component. Введение
- Урок 25. Navigation. Передача данных. Type-safe аргументы.
- Урок 26. Navigation. Параметры навигации
- Урок 27. Navigation. NavigationUI.
- Урок 28. Navigation. Вложенный граф. Global Action. Deep Link.
- Урок 29. WorkManager. Введение
- Урок 30. WorkManager. Критерии запуска задачи.
- Урок 31. WorkManager. Последовательность выполнения задач.
- Урок 32. WorkManager. Передача и получение данных
- Урок 33. Практика. О чем это будет.
- Урок 34. Практика. TodoApp. Список задач.
- Урок 35. Практика. TodoApp. Просмотр задачи
Если вы работали со стандартными механизмами 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. Looks like you've changed schema but forgot to update the version number. You can simply fix this by increasing the version number
Room сообщает, что мы решили изменить структуру базы данных, а номер версии не поменяли.
Ок, исправляемся, и поднимаем версию базы в Database классе.
@Database(entities = {Employee.class}, version = 2) public abstract class AppDatabase extends RoomDatabase { public abstract EmployeeDao employeeDao(); }
Запускаем приложение и снова ошибка: java.lang.IllegalStateException: A migration from 1 to 2 is necessary. Please provide a Migration in the builder or call fallbackToDestructiveMigration in the builder in which case Room will re-create all of the tables
На этот раз 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)
Если пользователь с первой версии базы обновится на четвертую, то все эти миграции будут выполнены одна за другой.
В следующем уроке рассмотрим, как написать тест для миграции.
Присоединяйтесь к нам в Telegram:
- в канале StartAndroid публикуются ссылки на новые статьи с сайта startandroid.ru и интересные материалы с хабра, medium.com и т.п.
- в чатах решаем возникающие вопросы и проблемы по различным темам: Android, Compose, Kotlin, RxJava, Dagger, Тестирование, Performance
- ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня