Best practices for in-app database migration for Sqlite

前端 未结 8 2078
囚心锁ツ
囚心锁ツ 2020-11-30 16:31

I am using sqlite for my iphone and I anticipate the database schema might change over time. What are the gotchas, naming conventions and things to watch out for to do a su

8条回答
  •  轻奢々
    轻奢々 (楼主)
    2020-11-30 17:22

    Let me share some migration code with FMDB and MBProgressHUD.

    Here's how you read and write the schema version number (this is presumably part of a model class, in my case it's a singleton class called 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);
    }
    

    Here's [self database] method that lazily opens the database:

    - (FMDatabase *)database {
        if (!_databaseOpen) {
            _databaseOpen = YES;
    
            NSString *documentsDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
            NSString *databaseName = [NSString stringWithFormat:@"userdata.sqlite"];
    
            _database = [[FMDatabase alloc] initWithPath:[documentsDir stringByAppendingPathComponent:databaseName]];
            _database.logsErrors = YES;
    
            if (![_database openWithFlags:SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FILEPROTECTION_COMPLETE]) {
                _database = nil;
            } else {
                NSLog(@"Database schema version is %d", [self databaseSchemaVersion]);
            }
        }
        return _database;
    }
    

    And here are migration methods called from the view controller:

    - (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]);
    }
    

    And here's the root view controller code that invokes the migration, using MBProgressHUD to display a progress bezel:

    - (void)viewDidAppear {
        [super viewDidAppear];
        if ([[Database sharedDatabase] userDatabaseNeedsMigration]) {
            MBProgressHUD *hud = [[MBProgressHUD alloc] initWithView:self.view.window];
            [self.view.window addSubview:hud];
            hud.removeFromSuperViewOnHide = YES;
            hud.graceTime = 0.2;
            hud.minShowTime = 0.5;
            hud.labelText = @"Upgrading data";
            hud.taskInProgress = YES;
            [[UIApplication sharedApplication] beginIgnoringInteractionEvents];
    
            [hud showAnimated:YES whileExecutingBlock:^{
                [[Database sharedDatabase] migrateUserDatabase];
            } onQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0) completionBlock:^{
                [[UIApplication sharedApplication] endIgnoringInteractionEvents];
            }];
        }
    }
    

提交回复
热议问题