我們常常會(huì)在APP中使用數(shù)據(jù)庫,但是由于版本迭代問題,數(shù)據(jù)庫的結(jié)構(gòu)可能會(huì)發(fā)生變更,這時(shí)候需要對用戶原始數(shù)據(jù)進(jìn)行保留。這是一個(gè)很正常的需求,有人可能會(huì)簡單粗暴的把數(shù)據(jù)庫刪除,重新創(chuàng)建,把數(shù)據(jù)重新插進(jìn)去。如果表很多,里面只有一張表的數(shù)據(jù)結(jié)構(gòu)發(fā)生變化了,這種做法真的好么?下面我會(huì)講下我的思路,分享交流下。
簡單的流程分析,共四步:
1 把要更改結(jié)構(gòu)的那張表 A1 改名為 tempA1
2 創(chuàng)建一張當(dāng)前版本需要結(jié)構(gòu)的表A1
3 將tempA1 里面的有效數(shù)據(jù) 遷移到 A1中
4 刪除 tempA1
以上簡單的思路數(shù)據(jù)庫就更改完畢了。
這時(shí)引出了第二個(gè)問題,如果用戶的app沒有及時(shí)更新,錯(cuò)過了好幾個(gè)版本的數(shù)據(jù)庫更改,以上數(shù)據(jù)庫更改不可能會(huì)一步到位了?;啚榉?,一步一步的更改數(shù)據(jù)庫表結(jié)構(gòu),直到更改到最后一次。下面看代碼。
typedef NS_ENUM(NSInteger, DBVersion) {
DBVersionV1,
DBVersionV2, //歷史版本
DBVersionV3, //當(dāng)前版本
};
static NSString *const DBVersionNum = @"DBVersionNum";
static NSString *const dbPath = @"\tmp\tmp.db"; //數(shù)據(jù)庫地址
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')))";
首先定義了一個(gè)枚舉,標(biāo)識(shí)著當(dāng)前一共有多少數(shù)據(jù)庫版本變更。(客戶端數(shù)據(jù)庫結(jié)構(gòu)更改不會(huì)太頻繁,如果更改太快,可能意味著初期表設(shè)計(jì)不合理),此次模擬共三個(gè)版本的數(shù)據(jù)庫。
- (instancetype)init{
if (self = [super init]) {
_queue = [FMDatabaseQueue databaseQueueWithPath:dbPath];
}
return self;
}
采用FMDatabaseQueue 進(jìn)行數(shù)據(jù)庫操作的管理。
/*
* 需要初始化表結(jié)構(gòu)時(shí),調(diào)用此方法
*/
- (void)newDBVersionInit{
if (![[NSUserDefaults standardUserDefaults] objectForKey:DBVersionNum]) {
//系統(tǒng)之前沒有數(shù)據(jù)庫 新建立表。
[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系統(tǒng)之前是否存儲(chǔ)過,
沒有存儲(chǔ)說明是第一次安裝,則進(jìn)行首次創(chuàng)建表處理。
有說明之前數(shù)據(jù)庫存在,進(jìn)行數(shù)據(jù)庫表結(jié)構(gòu)更改。如果是v1版本的數(shù)據(jù)庫 先從v1升級到v2,在從v2升級到v3,以此類推。
/*
* 創(chuàng)建新表
*/
- (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 數(shù)據(jù)遷移
*/
- (void)v1ToV2{
[_queue inTransaction:^(FMDatabase *db, BOOL *rollback) {
@try {
//將原始表名T1 修改為 tempT1
NSString *renameString = @"alter table t1 rename to tempT1";
[db executeUpdate:renameString];
//創(chuàng)建新表T1(V2版本的新表創(chuàng)建)
[db executeUpdate:createTable];
//遷移數(shù)據(jù)
NSString *toString = @"insert into t1(name,sex) select name,sex from tempT1";
[db executeUpdate:toString];
//刪除tempT1臨時(shí)表
NSString *dropTableStr1 = @"drop table tempT1";
[db executeUpdate:dropTableStr1];
}
@catch (NSException *exception) {
*rollback = YES;
}
}] ;
[[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithInteger:DBVersionV2] forKey:DBVersionNum];
}
- (void)v2Tov3{
[_queue inTransaction:^(FMDatabase *db, BOOL *rollback) {
//和 v1ToV2 流程一樣
}] ;
[[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithInteger:DBVersionV3] forKey:DBVersionNum];
}
上文提到的數(shù)據(jù)遷移流程就是如此。