Реализация миграции схемы базы данных (Room Migration) в Android-приложении

TRUETECH занимается разработкой, поддержкой и обслуживанием мобильных приложений iOS, Android, PWA. Имеем большой опыт и экспертизу для публикации мобильных приложений в популярные маркеты Google Play, App Store, Amazon, AppGallery и другие.
Разработка и поддержка любых видов мобильных приложений:
Информационные и развлекательные мобильные приложения
Новостные приложения, игры, справочники, онлайн-каталоги, погодные, фитнес и здоровье, туристические, образовательные, социальные сети и мессенджеры, квиз, блоги и подкасты, форумы, агрегаторы
Мобильные приложения электронной коммерции
Интернет-магазины, B2B-приложения, маркетплейсы, онлайн-обменники, кэшбэк-сервисы, биржи, дропшиппинг-платформы, программы лояльности, доставка еды и товаров, платежные системы
Мобильные приложения для управления бизнес-процессами
CRM-системы, ERP-системы, управление проектами, инструменты для команды продаж, учет финансов, управление производством, логистика и доставка, управление персоналом, системы мониторинга данных
Мобильные приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, платформы предоставления электронных услуг, платформы кешбека, видеохостинги, тематические порталы, платформы онлайн-бронирования и записи, платформы онлайн-торговли

Это лишь некоторые из типы мобильных приложений, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента.

Предлагаемые услуги
Показано 1 из 1 услугВсе 1735 услуг
Реализация миграции схемы базы данных (Room Migration) в Android-приложении
Средняя
~2-3 рабочих дня
Часто задаваемые вопросы
Наши компетенции:
Этапы разработки
Последние работы
  • image_mobile-applications_feedme_467_0.webp
    Разработка мобильного приложения для компании FEEDME
    756
  • image_mobile-applications_xoomer_471_0.webp
    Разработка мобильного приложения для компании XOOMER
    624
  • image_mobile-applications_rhl_428_0.webp
    Разработка мобильного приложения для компании RHL
    1054
  • image_mobile-applications_zippy_411_0.webp
    Разработка мобильного приложения для компании ZIPPY
    947
  • image_mobile-applications_affhome_429_0.webp
    Разработка мобильного приложения для компании Affhome
    862
  • image_mobile-applications_flavors_409_0.webp
    Разработка мобильного приложения для компании FLAVORS
    445

Реализация миграции схемы базы данных (Room Migration) в Android-приложении

Пользователь обновил приложение — и при первом запуске видит белый экран или крэш. В Logcat: IllegalStateException: Room cannot verify the data integrity. Looks like you've changed schema but forgot to update the version number. Или хуже: Migration didn't properly handle с потерей всех локальных данных. Это классика неправильно реализованной миграции Room.

Как Room обнаруживает изменения схемы

Room хранит хэш схемы базы данных. При каждом запуске сравнивает хэш скомпилированного @Database с хэшем, хранящимся в room_master_table. Если они не совпадают — Room бросает исключение, если не найдена подходящая миграция.

version в @Database — это не произвольный номер. Это контракт: если схема изменилась, version должен быть увеличен, и должна быть добавлена явная Migration(fromVersion, toVersion).

Типы изменений и их миграция

Добавление колонки (простой случай)

val MIGRATION_1_2 = object : Migration(1, 2) {
    override fun migrate(db: SupportSQLiteDatabase) {
        db.execSQL("ALTER TABLE transactions ADD COLUMN category TEXT NOT NULL DEFAULT ''")
    }
}

NOT NULL DEFAULT '' — обязательно. SQLite не позволяет добавить NOT NULL колонку без DEFAULT в уже существующую таблицу с данными.

Переименование колонки

SQLite не поддерживает ALTER TABLE RENAME COLUMN до версии 3.25.0. На Android API < 29 это недоступно. Универсальный путь — пересоздание таблицы:

val MIGRATION_2_3 = object : Migration(2, 3) {
    override fun migrate(db: SupportSQLiteDatabase) {
        // 1. Создаём новую таблицу с правильным именем колонки
        db.execSQL("""
            CREATE TABLE transactions_new (
                id TEXT NOT NULL PRIMARY KEY,
                amount REAL NOT NULL,
                description TEXT NOT NULL DEFAULT '',
                created_at INTEGER NOT NULL
            )
        """)
        // 2. Копируем данные (старая колонка 'note' → новая 'description')
        db.execSQL("""
            INSERT INTO transactions_new (id, amount, description, created_at)
            SELECT id, amount, note, created_at FROM transactions
        """)
        // 3. Удаляем старую
        db.execSQL("DROP TABLE transactions")
        // 4. Переименовываем новую
        db.execSQL("ALTER TABLE transactions_new RENAME TO transactions")
    }
}

Пересоздание таблицы — единственный надёжный путь для любых структурных изменений на всём диапазоне Android API.

Добавление таблицы с внешним ключом

db.execSQL("""
    CREATE TABLE IF NOT EXISTS tags (
        id TEXT NOT NULL PRIMARY KEY,
        name TEXT NOT NULL,
        color INTEGER NOT NULL DEFAULT 0
    )
""")
db.execSQL("""
    CREATE TABLE IF NOT EXISTS transaction_tags (
        transaction_id TEXT NOT NULL,
        tag_id TEXT NOT NULL,
        PRIMARY KEY (transaction_id, tag_id),
        FOREIGN KEY (transaction_id) REFERENCES transactions(id) ON DELETE CASCADE,
        FOREIGN KEY (tag_id) REFERENCES tags(id) ON DELETE CASCADE
    )
""")

Цепочки миграций и пропущенные версии

Room умеет строить цепочки: если пользователь не обновлял приложение с v1 до v3, Room применит Migration(1,2) + Migration(2,3). Но это работает только если вы зарегистрировали все промежуточные миграции.

Room.databaseBuilder(context, AppDatabase::class.java, "app.db")
    .addMigrations(MIGRATION_1_2, MIGRATION_2_3, MIGRATION_3_4)
    .build()

Если хотите поддержать прямой переход 1→4 (быстрее, одна SQL-операция) — добавьте Migration(1, 4) явно.

Тестирование миграций

Room предоставляет MigrationTestHelper для JUnit тестов:

@RunWith(AndroidJUnit4::class)
class MigrationTest {
    @get:Rule val helper = MigrationTestHelper(
        InstrumentationRegistry.getInstrumentation(),
        AppDatabase::class.java
    )

    @Test
    fun migrate1To2() {
        // Создаём базу версии 1
        helper.createDatabase(TEST_DB, 1).apply {
            execSQL("INSERT INTO transactions VALUES ('id1', 100.0, 'test', 1700000000)")
            close()
        }
        // Применяем миграцию и проверяем результат
        val db = helper.runMigrationsAndValidate(TEST_DB, 2, true, MIGRATION_1_2)
        val cursor = db.query("SELECT description FROM transactions WHERE id = 'id1'")
        assertTrue(cursor.moveToFirst())
        assertEquals("", cursor.getString(0))
    }
}

Тест на каждую миграцию — не опция, а обязательство. MigrationTestHelper.runMigrationsAndValidate валидирует итоговую схему против ожидаемой.

Экспортируемые JSON-схемы

Включите экспорт схемы в build.gradle:

android {
    defaultConfig {
        javaCompileOptions {
            annotationProcessorOptions {
                arguments += ["room.schemaLocation": "$projectDir/schemas".toString()]
            }
        }
    }
}

Room сохраняет schemas/1.json, schemas/2.json — это снимки схемы каждой версии. Их нужно коммитить в репозиторий. MigrationTestHelper использует их для валидации. Без этих файлов — тестирование миграций невозможно.

Fallback на destructive migration

В крайнем случае — только для dev-сборок или при явном согласии пользователя:

.fallbackToDestructiveMigration() // стирает все данные и пересоздаёт базу

В продакшене это недопустимо без предупреждения пользователя.

Что входит в работу

  • Аудит текущей схемы и истории версий
  • Написание Migration объектов для всех изменений схемы
  • Тесты через MigrationTestHelper для каждой миграции
  • Настройка экспорта JSON-схем
  • Обработка edge cases: пустые таблицы, внешние ключи, индексы, триггеры

Сроки

1–2 простые миграции (добавление колонок): 0,5–1 день. Сложная реструктуризация (переименование, пересоздание таблиц, цепочки миграций) с полным покрытием тестами: 2–3 дня.