Реализация миграции схемы базы данных (SQLite Migration) в мобильном приложении
Прямая работа с SQLite без ORM-прослойки — React Native, Flutter, Capacitor, или нативный Android/iOS с SQLiteOpenHelper. Здесь нет автоматической инфраструктуры миграций — только SQL и ваши руки. Главная ловушка: SQLite крайне ограничен в ALTER TABLE, и на Android API < 29 доступных операций ещё меньше.
SQLiteOpenHelper (Android) — базовая механика
class AppDatabase(context: Context) : SQLiteOpenHelper(context, "app.db", null, DB_VERSION) {
override fun onCreate(db: SQLiteDatabase) {
db.execSQL(CREATE_TABLE_TRANSACTIONS)
}
override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
if (oldVersion < 2) migrate1to2(db)
if (oldVersion < 3) migrate2to3(db)
if (oldVersion < 4) migrate3to4(db)
}
}
Это цепочка if-проверок, а не when или switch. Пользователь с версией 1 последовательно пройдёт все миграции до текущей. Пропускать версии нельзя — только добавлять новые блоки.
Flutter: sqflite и drift
sqflite — самый популярный SQLite-пакет для Flutter. Миграции через onUpgrade:
final db = await openDatabase(
'app.db',
version: 3,
onCreate: (db, version) async {
await db.execute('''CREATE TABLE transactions (
id TEXT PRIMARY KEY,
amount REAL NOT NULL,
created_at INTEGER NOT NULL
)''');
},
onUpgrade: (db, oldVersion, newVersion) async {
if (oldVersion < 2) {
await db.execute('ALTER TABLE transactions ADD COLUMN category TEXT DEFAULT ""');
}
if (oldVersion < 3) {
// Пересоздание таблицы для переименования колонки
await _recreateTransactionsTable(db);
}
},
);
drift (бывший moor) — типизированный ORM поверх SQLite для Flutter/Dart с декларативными миграциями. Генерирует код из схемы, имеет Migrator с createTable, addColumn, renameColumn. Для проектов от среднего размера — предпочтительнее sqflite.
React Native: react-native-sqlite-storage / expo-sqlite
expo-sqlite с SQLite 3.39+:
const db = SQLite.openDatabase('app.db');
db.transaction(tx => {
tx.executeSql('PRAGMA user_version', [], (_, result) => {
const version = result.rows.item(0).user_version;
if (version < 1) {
tx.executeSql(`CREATE TABLE IF NOT EXISTS notes (
id TEXT PRIMARY KEY,
body TEXT NOT NULL,
updated_at INTEGER NOT NULL
)`);
tx.executeSql('PRAGMA user_version = 1');
}
if (version < 2) {
tx.executeSql('ALTER TABLE notes ADD COLUMN title TEXT DEFAULT ""');
tx.executeSql('PRAGMA user_version = 2');
}
});
});
PRAGMA user_version — встроенный механизм SQLite для хранения версии схемы.
Пересоздание таблицы: универсальный рецепт
SQLite поддерживает только ADD COLUMN из всех ALTER TABLE операций (до 3.35.0). Переименовать колонку, удалить её, изменить тип — только через пересоздание:
BEGIN TRANSACTION;
CREATE TABLE transactions_new (
id TEXT NOT NULL PRIMARY KEY,
amount REAL NOT NULL,
description TEXT NOT NULL DEFAULT '', -- переименовано с 'note'
created_at INTEGER NOT NULL
);
INSERT INTO transactions_new (id, amount, description, created_at)
SELECT id, amount, note, created_at FROM transactions;
DROP TABLE transactions;
ALTER TABLE transactions_new RENAME TO transactions;
-- Восстанавливаем индексы
CREATE INDEX idx_transactions_created_at ON transactions(created_at);
COMMIT;
Всё внутри транзакции — если что-то пошло не так, данные не потеряны. Восстановление индексов после RENAME — обязательно: они не переносятся автоматически.
Внешние ключи при пересоздании
Если есть внешние ключи — временно отключаем их во время пересоздания:
PRAGMA foreign_keys = OFF;
BEGIN TRANSACTION;
-- ... пересоздание таблицы ...
COMMIT;
PRAGMA foreign_keys = ON;
PRAGMA integrity_check;
PRAGMA integrity_check после — убеждаемся, что данные консистентны.
Тестирование
Тестирование миграций SQLite без ORM — ручное. Нужен тест, который:
- Открывает базу версии N (из заготовленного файла или создаёт программно)
- Вставляет тестовые данные
- Вызывает
onUpgradeдо версии N+1 - Проверяет структуру и данные через
PRAGMA table_info
Для Flutter/drift — есть SchemaVerifier из пакета drift_dev.
Что входит в работу
- Анализ текущей схемы и версии
- Написание SQL-миграций для всех изменений
- Транзакционное пересоздание таблиц при необходимости
- Восстановление индексов и внешних ключей
- Тесты с реальными данными
Сроки
Простые ADD COLUMN миграции: 0,5 дня. Структурные изменения (пересоздание таблиц, внешние ключи, индексы) с тестами: 1–2 дня.







