我們常常會在APP中使用數據庫,但是由于版本迭代問題,數據庫的結構可能會發生變更,這時候需要對用戶原始數據進行保留。這是一個很正常的需求,有人可能會簡單粗暴的把數據庫刪除,重新創建,把數據重新插進去。如果表很多,里面只有一張表的數據結構發生變化了,這種做法真的好么?下面我會講下我的思路,分享交流下。
簡單的流程分析,共四步:
- 把要更改結構的那張表 A1 改名為 tempA1
- 創建一張當前版本需要結構的表A1
- 將tempA1 里面的有效數據 遷移到 A1中
- 刪除 tempA1
以上簡單的思路數據庫就更改完畢了。
這時引出了第二個問題,如果用戶的app沒有及時更新,錯過了好幾個版本的數據庫更改,以上數據庫更改不可能會一步到位了。化簡為繁,一步一步的更改數據庫表結構,直到更改到最后一次。下面看代碼。
typedef NS_ENUM(NSInteger, DBVersion) {
DBVersionV1,
DBVersionV2, //歷史版本
DBVersionV3, //當前版本
};
static NSString *const DBVersionNum = @"DBVersionNum";
static NSString *const dbPath = @"\tmp\tmp.db"; //數據庫地址
static NSString *const createTable = @"create table if not exists t1("
"id integer PRIMARY KEY AUTOINCREMENT NOT NULL,"
"name char(50),"
"sex char(4),"
"recordDate TIMESTAMP default (datetime('now', 'localtime')))";
首先定義了一個枚舉,標識著當前一共有多少數據庫版本變更。(客戶端數據庫結構更改不會太頻繁,如果更改太快,可能意味著初期表設計不合理),此次模擬共三個版本的數據庫。
- (instancetype)init{
if (self = [super init]) {
_queue = [FMDatabaseQueue databaseQueueWithPath:dbPath];
}
return self;
}
采用FMDatabaseQueue 進行數據庫操作的管理。
/*
* 需要初始化表結構時,調用此方法
*/
- (void)newDBVersionInit{
if (![[NSUserDefaults standardUserDefaults] objectForKey:DBVersionNum]) {
//系統之前沒有數據庫 新建立表。
[self createTables];
}else{
DBVersion ver = [[[NSUserDefaults standardUserDefaults] objectForKey:DBVersionNum] integerValue];
switch (ver) {
case DBVersionV1:{
[self v1ToV2];
}
case DBVersionV2:{
[self v2Tov3];
}
case DBVersionV3:{
}
break;
default:
break;
}
}
}
在這里判斷DBVersionNum系統之前是否存儲過,
沒有存儲說明是第一次安裝,則進行首次創建表處理。
有說明之前數據庫存在,進行數據庫表結構更改。如果是v1版本的數據庫 先從v1升級到v2,在從v2升級到v3,以此類推。
/*
* 創建新表
*/
- (void)createTables{
[_queue inTransaction:^(FMDatabase *db, BOOL *rollback) {
@try {
[db executeUpdate:createTable];
}
@catch (NSException *exception) {
*rollback = YES;
}
}];
[[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithInteger:DBVersionV3] forKey:DBVersionNum];
}
把 DBVersionNum的值寫為V3版本 方便下次對比。
/*
* 版本1 向 版本2 數據遷移
*/
- (void)v1ToV2{
[_queue inTransaction:^(FMDatabase *db, BOOL *rollback) {
@try {
//將原始表名T1 修改為 tempT1
NSString *renameString = @"alter table t1 rename to tempT1";
[db executeUpdate:renameString];
//創建新表T1(V2版本的新表創建)
[db executeUpdate:createTable];
//遷移數據
NSString *toString = @"insert into t1(name,sex) select name,sex from tempT1";
[db executeUpdate:toString];
//刪除tempT1臨時表
NSString *dropTableStr1 = @"drop table tempT1";
[db executeUpdate:dropTableStr1];
}
@catch (NSException *exception) {
*rollback = YES;
}
}] ;
[[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithInteger:DBVersionV2] forKey:DBVersionNum];
}
//V2升級到V3
- (void)v2Tov3{
[_queue inTransaction:^(FMDatabase *db, BOOL *rollback) {
//和 v1ToV2 流程一樣
}] ;
[[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithInteger:DBVersionV3] forKey:DBVersionNum];
}
上文提到的數據遷移流程就是如此。
如有更好的方案可以互相交流。