programing

Sqlite용 앱 내 데이터베이스 마이그레이션 모범 사례

easyjava 2023. 6. 3. 08:51
반응형

Sqlite용 앱 내 데이터베이스 마이그레이션 모범 사례

나는 내 아이폰에 sqlite를 사용하고 있으며 시간이 지남에 따라 데이터베이스 스키마가 변경될 것으로 예상합니다.마이그레이션을 성공적으로 수행하기 위해 매번 주의해야 할 사항, 명명 규칙 및 주의 사항은 무엇입니까?

예를 들어 데이터베이스 이름(예: Database_v1)에 버전을 추가하려고 합니다.

정기적으로 SQLite 데이터베이스를 업데이트하고 이전 데이터베이스를 새 스키마로 마이그레이션해야 하는 응용프로그램을 유지 관리합니다. 다음 작업을 수행합니다.

데이터베이스 버전을 추적하기 위해 sqlite가 제공하는 기본 제공 사용자 버전 변수를 사용합니다(sqlite는 이 변수와 아무런 관련이 없으며 원하는 대로 사용할 수 있습니다).0부터 시작하며 다음 sqlite 문으로 이 변수를 가져오거나 설정할 수 있습니다.

> PRAGMA user_version;  
> PRAGMA user_version = 1;

앱이 시작되면 현재 사용자 버전을 확인하고 스키마를 최신으로 업데이트하는 데 필요한 변경 사항을 적용한 다음 사용자 버전을 업데이트합니다.저는 트랜잭션으로 업데이트를 마무리하여 문제가 발생하더라도 변경 사항이 커밋되지 않도록 합니다.

스키마를 변경하기 위해 sqlite는 특정 작업(테이블 이름 변경 또는 열 추가)에 대해 "ALTER TABLE" 구문을 지원합니다.기존 테이블을 쉽게 업데이트할 수 있습니다.다음 문서를 참조하십시오. http://www.sqlite.org/lang_altertable.htmlALTER TABLE 구문에서 지원되지 않는 열 또는 기타 변경사항을 삭제하기 위해 새 테이블을 작성하고 날짜를 테이블로 마이그레이트한 후 이전 테이블을 삭제하고 새 테이블의 이름을 원래 이름으로 변경합니다.

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

FMDB 및 MBProgress와 마이그레이션 코드를 공유하겠습니다.HUD.

스키마 버전 번호를 읽고 쓰는 방법은 다음과 같습니다(이는 모델 클래스의 일부이며, 이 경우 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]데이터베이스를 천천히 여는 방법:

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

View 컨트롤러에서 호출되는 마이그레이션 방법은 다음과 같습니다.

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

다음은 MBProgress를 사용하여 마이그레이션을 호출하는 루트 보기 컨트롤러 코드입니다.HUD는 진행 베젤을 표시합니다.

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

1합니다.만들다/migrationsSQL 기반 마이그레이션 목록이 있는 폴더. 각 마이그레이션은 다음과 같습니다.

/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 Category;

/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 Client for Node.js Apps

최상의 솔루션 IMO는 SQLite 업그레이드 프레임워크를 구축하는 것입니다.저도 (C# 세계에서) 같은 문제를 겪었고, 저만의 프레임워크를 만들었습니다.당신은 여기서 그것에 대해 읽을 수 있습니다.완벽하게 작동하며 (이전에는 악몽 같았던) 업그레이드를 최소한의 노력으로 수행할 수 있습니다.

라이브러리가 C#으로 구현되어 있지만, 여기에 제시된 아이디어는 당신의 경우에도 잘 작동할 것입니다.

몇 가지 팁...

데이터베이스를 마이그레이션하기 위한 모든 코드를 NSO 작업에 넣고 백그라운드 스레드에서 실행하는 것이 좋습니다.데이터베이스가 마이그레이션되는 동안 Spinner와 함께 사용자 정의 UIAertView를 표시할 수 있습니다.

데이터베이스를 번들에서 앱의 문서로 복사하고 해당 위치에서 데이터베이스를 사용하는지 확인하십시오. 그렇지 않으면 각 앱 업데이트로 전체 데이터베이스를 덮어쓰고 새 빈 데이터베이스를 마이그레이션합니다.

FMDB는 훌륭하지만 실행합니다.쿼리 메서드가 어떤 이유로 PRAGMA 쿼리를 수행할 수 없습니다.PRAGMA user_version을 사용하여 스키마 버전을 확인하려면 sqlite3를 직접 사용하는 메서드를 작성해야 합니다.

이 코드 구조는 사용자가 앱 업데이트 사이를 얼마나 오가는지에 관계없이 업데이트가 순서대로 실행되고 모든 업데이트가 실행되도록 보장합니다.이것은 좀 더 재요인화 될 수도 있지만, 이것은 매우 간단한 방법입니다.이 방법은 데이터 싱글톤이 인스턴스화될 때마다 안전하게 실행될 수 있으며, 데이터 싱글톤을 올바르게 설정할 경우 세션당 한 번만 발생하는 작은 DB 쿼리 하나만 비용이 듭니다.

- (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:

엔티티 프레임워크 코어입니다.Sqlite. 마이그레이션

단순하기 때문에 다른 플랫폼에서도 lib와 동일한 동작을 쉽게 구현할 수 있습니다.

내장된 앱과 전화기에 위치한 앱에서 발생할 가능성이 높은 것처럼 데이터베이스 스키마와 이를 사용하는 모든 코드를 변경하면 문제가 실제로 잘 제어됩니다(수백 개의 앱을 제공하는 엔터프라이즈 DB에서 스키마를 마이그레이션하는 악몽과 비교할 수 있는 것은 아무것도 없습니다. DBA의 제어 하에 있는 것도 아닙니다.

기사 SQLite에 대한 단순 선언 스키마 마이그레이션에서는 기본 메모리 내 데이터베이스를 만들고 두 테이블에서 "sqlite_schema" 테이블을 쿼리하여 스키마를 현재 데이터베이스와 비교하여 스키마 변경을 자동으로 해결합니다.그런 다음 SQLite 설명서의 12단계 절차에 따라 테이블을 안전하게 수정합니다.

스키마를 사용하여 새 메모리 내 데이터베이스를 만들 수 있는 경우 스키마를 원하는 대로 정의할 수 있습니다(ORM 또는 일반 SQL "CREATE TABLE" 문 등).즉, 스키마를 한 곳에서만 유지 관리하면 되며, 응용프로그램이 시작될 때 변경사항이 자동으로 적용됩니다.

물론 제한이 있습니다. 특히 데이터 마이그레이션은 처리하지 않고 스키마 마이그레이션만 처리합니다. 새 열은 null을 허용하거나 기본값을 지정해야 합니다.하지만 전반적으로 함께 일하는 것은 즐겁습니다.

언급URL : https://stackoverflow.com/questions/989558/best-practices-for-in-app-database-migration-for-sqlite

반응형