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
'programing' 카테고리의 다른 글
| x초마다 함수를 반복 실행하는 방법은 무엇입니까? (0) | 2023.06.03 |
|---|---|
| iOS 앱이 감옥에서 고장난 전화기에서 실행되고 있는지 어떻게 감지합니까? (0) | 2023.06.03 |
| Git의 파일에 chmod 권한을 추가하는 방법은 무엇입니까? (0) | 2023.06.03 |
| npm 설치로 devDependencies가 설치되지 않습니다. (0) | 2023.06.03 |
| 두 날짜 사이의 일 수 (0) | 2023.06.03 |