Я поддерживаю приложение, которому периодически необходимо обновлять базу данных sqlite и переносить старые базы данных в новую схему, и вот что я делаю:
Для отслеживания версии базы данных я использую встроенную переменную версии пользователя, которую предоставляет sqlite (sqlite ничего не делает с этой переменной, вы можете использовать ее, как хотите). Он начинается с 0, и вы можете получить / установить эту переменную с помощью следующих операторов sqlite:
> PRAGMA user_version;
> PRAGMA user_version = 1;
Когда приложение запускается, я проверяю текущую версию пользователя, применяю любые изменения, которые необходимы для обновления схемы, а затем обновляю версию пользователя. Я заключаю обновления в транзакцию, чтобы, если что-то пойдет не так, изменения не зафиксированы.
Для внесения изменений в схему sqlite поддерживает синтаксис «ALTER TABLE» для определенных операций (переименование таблицы или добавление столбца). Это простой способ обновить существующие таблицы на месте. См. Документацию здесь: http://www.sqlite.org/lang_altertable.html . Для удаления столбцов или других изменений, которые не поддерживаются синтаксисом «ALTER TABLE», я создаю новую таблицу, переношу в нее дату, удаляю старую таблицу и переименовываю новую таблицу в исходное имя.
application_id
- это дополнительный бит для определения формата файла,file
например, утилитой, а не для версий базы данных.Ответ Just Curious точен (вы меня поняли!), И это то, что мы используем для отслеживания версии схемы базы данных, которая в данный момент находится в приложении.
Чтобы выполнить миграции, которые должны произойти, чтобы получить user_version, соответствующую ожидаемой версии схемы приложения, мы используем оператор switch. Вот сокращенный пример того, как это выглядит в нашем приложении Strip :
- (void) migrateToSchemaFromVersion:(NSInteger)fromVersion toVersion:(NSInteger)toVersion { // allow migrations to fall thru switch cases to do a complete run // start with current version + 1 [self beginTransaction]; switch (fromVersion + 1) { case 3: // change pin type to mode 'pin' for keyboard handling changes // removing types from previous schema sqlite3_exec(db, "DELETE FROM types;", NULL, NULL, NULL); NSLog(@"installing current types"); [self loadInitialData]; case 4: //adds support for recent view tracking sqlite3_exec(db, "ALTER TABLE entries ADD COLUMN touched_at TEXT;", NULL, NULL, NULL); case 5: { sqlite3_exec(db, "ALTER TABLE categories ADD COLUMN image TEXT;", NULL, NULL, NULL); sqlite3_exec(db, "ALTER TABLE categories ADD COLUMN entry_count INTEGER;", NULL, NULL, NULL); sqlite3_exec(db, "CREATE INDEX IF NOT EXISTS categories_id_idx ON categories(id);", NULL, NULL, NULL); sqlite3_exec(db, "CREATE INDEX IF NOT EXISTS categories_name_id ON categories(name);", NULL, NULL, NULL); sqlite3_exec(db, "CREATE INDEX IF NOT EXISTS entries_id_idx ON entries(id);", NULL, NULL, NULL); // etc... } } [self setSchemaVersion]; [self endTransaction]; }
источник
toVersion
в своем коде? Как это происходит, когда вы используете версию 0 и после нее есть еще две версии. Это означает, что вам нужно перейти с 0 на 1 и с 1 на 2. Как вы с этим справляетесь?break
операторовswitch
, поэтому все последующие миграции также будут происходить.Позвольте мне поделиться кодом миграции с FMDB и MBProgressHUD.
Вот как вы читаете и записываете номер версии схемы (предположительно, это часть класса модели, в моем случае это одноэлементный класс с именем Database):
- (int)databaseSchemaVersion { FMResultSet *resultSet = [[self database] executeQuery:@"PRAGMA user_version"]; int version = 0; if ([resultSet next]) { version = [resultSet intForColumnIndex:0]; } return version; } - (void)setDatabaseSchemaVersion:(int)version { // FMDB cannot execute this query because FMDB tries to use prepared statements sqlite3_exec([self database].sqliteHandle, [[NSString stringWithFormat:@"PRAGMA user_version = %d", DatabaseSchemaVersionLatest] UTF8String], NULL, NULL, NULL); }
Вот
[self database]
метод, который лениво открывает базу данных:А вот методы миграции, вызываемые из контроллера представления:
- (BOOL)databaseNeedsMigration { return [self databaseSchemaVersion] < databaseSchemaVersionLatest; } - (void)migrateDatabase { int version = [self databaseSchemaVersion]; if (version >= databaseSchemaVersionLatest) return; NSLog(@"Migrating database schema from version %d to version %d", version, databaseSchemaVersionLatest); // ...the actual migration code... if (version < 1) { [[self database] executeUpdate:@"CREATE TABLE foo (...)"]; } [self setDatabaseSchemaVersion:DatabaseSchemaVersionLatest]; NSLog(@"Database schema version after migration is %d", [self databaseSchemaVersion]); }
А вот код контроллера корневого представления, который вызывает миграцию, используя MBProgressHUD для отображения лицевой панели прогресса:
источник
schema_version
прагма обычно не является тем, с чем люди имеют дело.Лучшее решение IMO - создать структуру обновления SQLite. У меня была такая же проблема (в мире C #), и я построил свой собственный фреймворк. Вы можете прочитать об этом здесь . Он работает отлично и заставляет мои (ранее кошмарные) обновления работать с минимальными усилиями с моей стороны.
Хотя библиотека реализована на C #, идеи, представленные в ней, должны работать и в вашем случае.
источник
1
. Создайте/migrations
папку со списком миграций на основе SQL, где каждая миграция будет выглядеть примерно так:/migrations/001-categories.sql
-- Up CREATE TABLE Category (id INTEGER PRIMARY KEY, name TEXT); INSERT INTO Category (id, name) VALUES (1, 'Test'); -- Down DROP TABLE User;
/migrations/002-posts.sql
-- Up CREATE TABLE Post (id INTEGER PRIMARY KEY, categoryId INTEGER, text TEXT); -- Down DROP TABLE Post;
2
. Создайте таблицу db, содержащую список примененных миграций, например:CREATE TABLE Migration (name TEXT);
3
. Обновите логику начальной загрузки приложения, чтобы перед запуском оно получало список миграций из/migrations
папки и запускало миграции, которые еще не были применены.Вот пример, реализованный с помощью JavaScript: Клиент SQLite для приложений Node.js
источник
Несколько советов ...
1) Я рекомендую поместить весь код для переноса вашей базы данных в NSOperation и запустить его в фоновом потоке. Вы можете показать настраиваемый UIAlertView с помощью счетчика во время миграции базы данных.
2) Убедитесь, что вы копируете свою базу данных из пакета в документы приложения и используете ее из этого места, иначе вы просто перезапишете всю базу данных при каждом обновлении приложения, а затем перенесете новую пустую базу данных.
3) FMDB великолепен, но его метод executeQuery по какой-то причине не может выполнять запросы PRAGMA. Вам нужно будет написать свой собственный метод, который напрямую использует sqlite3, если вы хотите проверить версию схемы с помощью PRAGMA user_version.
4) Эта структура кода гарантирует, что ваши обновления выполняются по порядку и все обновления выполняются независимо от того, сколько времени пользователь проходит между обновлениями приложения. Его можно было бы реорганизовать и дальше, но это очень простой способ взглянуть на него. Этот метод можно безопасно запускать каждый раз, когда создается экземпляр вашего синглтона данных, и стоит только один крошечный запрос к базе данных, который выполняется только один раз за сеанс, если вы правильно настроили синглтон данных.
- (void)upgradeDatabaseIfNeeded { if ([self databaseSchemaVersion] < 3) { if ([self databaseSchemaVersion] < 2) { if ([self databaseSchemaVersion] < 1) { // run statements to upgrade from 0 to 1 } // run statements to upgrade from 1 to 2 } // run statements to upgrade from 2 to 3 // and so on... // set this to the latest version number [self setDatabaseSchemaVersion:3]; } }
источник
Если вы измените схему базы данных и весь код, который ее использует в режиме блокировки, что, вероятно, будет иметь место во встроенных приложениях и приложениях, размещенных на телефоне, проблема на самом деле находится под контролем (ничто не сравнимо с кошмаром миграции схемы в корпоративной базе данных. который может обслуживать сотни приложений - не все тоже находятся под контролем администратора баз данных ;-).
источник
Для .net вы можете использовать lib:
EntityFrameworkCore.Sqlite.Migrations
Это просто, поэтому для любой другой платформы вы можете легко реализовать то же поведение, что и в lib.
источник