SQLite封裝

1. 創(chuàng)建 XSqliteTool 類鸠按,對 SQLite 基本操作進行封裝

  • 打開數(shù)據(jù)庫
#pragma mark - 打開數(shù)據(jù)庫
+ (BOOL)openDB: (NSString *)uid {
    NSString *dbName = @"common.sqlite";
    if (uid) {
        dbName = [NSString stringWithFormat:@"%@.sqlite", uid];
    }
    NSString *fileName = [kPathName stringByAppendingPathComponent:dbName];
    return sqlite3_open(fileName.UTF8String, &ppDb) == SQLITE_OK;
}
  • 關(guān)閉數(shù)據(jù)庫
#pragma mark - 關(guān)閉數(shù)據(jù)庫
+ (void)closeDB {
    sqlite3_close(ppDb);
}
  • 執(zhí)行語句
#pragma mark - 執(zhí)行數(shù)據(jù)庫
+ (BOOL)dealSql: (NSString *)sql uid: (NSString *)uid {
    if (![self openDB:uid]) {
        NSLog(@"打開數(shù)據(jù)庫失敗");
        return false;
    }
    BOOL result = sqlite3_exec(ppDb, sql.UTF8String, nil, nil, nil) == SQLITE_OK;
    [self closeDB];
    return result;
}
  • 查詢語句
#pragma mark -  查詢操作
+ (NSMutableArray<NSMutableDictionary *> *)querySql:(NSString *)sql uid:(NSString *)uid {
    if (![self openDB:uid]) {
        NSLog(@"打開數(shù)據(jù)庫失敗");
        return false;
    }
    sqlite3_stmt *stmt = nil;
    if (sqlite3_prepare_v2(ppDb, sql.UTF8String, -1, &stmt, nil) != SQLITE_OK) {
        NSLog(@"準備語句失敗");
    }
    NSMutableArray *resultArrM = [NSMutableArray array];
    while (sqlite3_step(stmt) == SQLITE_ROW) {
        NSMutableDictionary *dictM = [NSMutableDictionary dictionary];
        int count = sqlite3_column_count(stmt);
        for (int i = 0; i < count; i++) {
            const char *columnNameC = sqlite3_column_name(stmt, i);
            NSString *columnName = [NSString stringWithUTF8String:columnNameC];  
            int type = sqlite3_column_type(stmt, i);
            id value = nil;
            switch (type) {
                case SQLITE_TEXT:
                    value = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, i)];
                    break;
                case SQLITE_INTEGER:
                    value = @(sqlite3_column_int(stmt, i));
                    break;
                case SQLITE_FLOAT:
                    value = @(sqlite3_column_double(stmt, i));
                    break;
                case SQLITE_BLOB:
                    value = CFBridgingRelease(sqlite3_column_blob(stmt, i));
                    break;
                default:
                    break;
            }
            [dictM setValue:value forKey:columnName];
        }
        [resultArrM addObject:dictM];
    }
    sqlite3_finalize(stmt);
    [self closeDB];
    return resultArrM;
}

2. 創(chuàng)建 XSqliteModelTool 類 動態(tài)創(chuàng)建表

  • 基本創(chuàng)建概要
    - 拼接完整的sql語句
    - 表名:以類的名字定義
    - 字段名稱:類的成員變量
    - 主鍵:通過協(xié)議介汹,讓類遵守協(xié)議仰担,實現(xiàn)主鍵方法
    - 需要忽略的字段:通過協(xié)議咱娶,讓類遵守協(xié)議躯肌,實現(xiàn)忽略字段的方法

  • XSqliteModelTool 中創(chuàng)建表的方法

+ (BOOL)createTable:(Class)cls uid:(NSString *)uid {
    // create table if not exists tableName(字段1 類型缭贡,字段2 類型 炉擅。。阳惹。)
    NSString *tableName = [XModelTool tableName:cls];
    NSString *columnNameAndType = [XModelTool columnNameAndTypeStr:cls];
    
    if (![cls respondsToSelector:@selector(primaryKey)]) {
        NSLog(@"請先實現(xiàn)+ primaryKey 方法");
        return NO;
    }
    NSString *primaryKey = [cls primaryKey];
    NSString *sql = [NSString stringWithFormat:@"create table if not exists %@(%@, primary key(%@))", tableName, columnNameAndType, primaryKey];
    
    return [XSqliteTool dealSql:sql uid:uid];
}
  • 其中的 XModelTool 類 為 XSqliteModelTool 服務(wù)谍失,實現(xiàn)了如下方法
    // 獲取表名
    + (NSString *)tableName: (Class)cls;
    // 獲取成員變量和成員變量的類型 字典
    + (NSDictionary *)classIvarNameAndTypeDict: (Class)cls;
    // 獲取類的成員變量和成員變量的類型映射成sqlite的類型 字典
    + (NSDictionary *)classIvarNameAndSqliteTypeDict: (Class)cls;
    // 獲取表的字段及類型
    + (NSString *)columnNameAndTypeStr: (Class)cls;
    // 所有排好序的表的字段
    + (NSArray *)tableSortedIvarNames: (Class)cls;

3. 動態(tài)更新表

  • 檢測表格是否需要更新,需要更新的情況如下:
    - 修改了字段名稱
    - 新增了字段
    - 刪除了字段

  • 動態(tài)的遷移數(shù)據(jù)
    1. 創(chuàng)建新的臨時表格
    2. 以新表為基準莹汤,從舊表中快鱼,取數(shù)據(jù)進行填充
    2.0 根據(jù)主鍵,插入主鍵的數(shù)據(jù)
    2.1 按照新表的有效字段(即是 舊表中包含的字段)纲岭,從舊表中更新數(shù)據(jù)到新表
    3. 刪除舊表
    4. 修改臨時表格的名稱為新表

  • 對字段改名的處理
    - 通過協(xié)議獲取改名的映射字典
    - 遷移數(shù)據(jù)時進行過濾:沒有被舊表的字段包含抹竹,且更改后的字段名也沒有被舊表包含

  • 主要代碼如下

#pragma mark - 判斷是否需要更新表格
+ (BOOL)isTableRequiredUpdate: (Class)cls uid: (NSString *)uid {
    NSArray *tableSortedNames = [XTableModel tableSortedNames:cls uid:uid];
    NSArray *modelSortedNames = [XModelTool tableSortedIvarNames:cls];
    
    return ![tableSortedNames isEqualToArray:modelSortedNames];
}

#pragma mark - 是否更新成功
+ (BOOL)isSuccessUpdateTable:(Class)cls uid:(NSString *)uid {
    
    NSArray *sqls = [self udpateSqls:cls uid:uid];
    return [XSqliteTool dealSqls:sqls uid:uid];
}

#pragma mark - 返回所有需要操作的sql語句
+ (NSArray *)udpateSqls: (Class)cls uid: (NSString *)uid {
    if ([self isTableRequiredUpdate:cls uid:uid] == NO) {
        NSLog(@"不需要更新表");
        return nil;
    }
    // 創(chuàng)建正確結(jié)構(gòu)的臨時表
    NSMutableArray *sqls = [NSMutableArray array];
    NSString *tableName = [XModelTool tableName:cls];
    // 1.創(chuàng)建臨時表
    NSString *tempTableName = [XModelTool tempTableName:cls];
    NSString *columnNameAndType = [XModelTool columnNameAndTypeStr:cls];
    
    if (![cls respondsToSelector:@selector(primaryKey)]) {
        NSLog(@"請先實現(xiàn)+ primaryKey 方法");
        return nil;
    }
    NSString *primaryKey = [cls primaryKey];
    NSString *tempSql = [NSString stringWithFormat:@"create table if not exists %@(%@, primary key(%@))", tempTableName, columnNameAndType, primaryKey];
    [sqls addObject:tempSql];
    
    // 2.插入舊表中的主鍵數(shù)據(jù)到臨時表
    NSString *insertPKeySql = [NSString stringWithFormat:@"insert into %@(%@) select %@ from %@", tempTableName, primaryKey, primaryKey, tableName];
    [sqls addObject:insertPKeySql];
    
    // 根據(jù)主鍵更新新表內(nèi)容
    NSDictionary *oldNameToNewNameDict = @{};
    if ([cls respondsToSelector:@selector(oldNameToNewName)]) {
        oldNameToNewNameDict = [cls oldNameToNewName];
    }
    NSArray *oldNames = [XTableModel tableSortedNames:cls uid:uid];
    NSArray *newNames = [XModelTool tableSortedIvarNames:cls];
    for (NSString *newName in newNames) {
        NSString *oldName = newName;
        if ([oldNameToNewNameDict[newName] length] != 0) {
            oldName = oldNameToNewNameDict[newName];
        }
        
        if (![oldNames containsObject:newName] && ![oldNames containsObject:oldName]) {
            continue;
        }
        NSString *updateSql = [NSString stringWithFormat:@"update %@ set %@ = (select %@ from %@ where %@.%@ = %@.%@)", tempTableName, newName, oldName, tableName, tableName, primaryKey, tempTableName, primaryKey];
        [sqls addObject:updateSql];
    }
    // 刪除舊表
    NSString *dropSql = [NSString stringWithFormat:@"drop table if exists %@", tableName];
    [sqls addObject:dropSql];
    // 更新表明
    NSString *tableNameSql = [NSString stringWithFormat:@"alter table %@ rename to %@", tempTableName, tableName];
    [sqls addObject:tableNameSql];
    
    return sqls;
}

創(chuàng)建 XTableModelTool 類,用于存放如下方法

// 判斷表格是否存在
+ (BOOL)isTableExists: (Class)cls uid: (NSString *)uid;
// 獲取排好序的表名
+ (NSArray *)tableSortedNames: (Class)cls uid: (NSString *)uid;

多條語句的處理

- 在 XSqliteTool 類中增加方法
     + (BOOL)dealSqls: (NSArray *)sqls uid: (NSString *)uid;
- 運用事物多條語句的處理進行干預
     1. 在執(zhí)行語句前止潮,打開事務(wù)
     2. 如果有語句執(zhí)行結(jié)果失敗窃判,則回滾事務(wù)
     3. 所有結(jié)果都成功,就提交事務(wù) 

模型操作-保存/更新模型

  • 操作步驟
    1. 檢查表格是否存在喇闸,沒有則創(chuàng)建
    2. 檢查表格是否需要更新袄琳,需要則更新
    3. 插入或者更新
    - 根據(jù)主鍵判斷記錄是否存在:
    - 存在询件,則拼接更新語句;不存在唆樊,則拼接插入語句

  • 代碼

+ (BOOL)saveOrUpdateModel:(id)model uid:(NSString *)uid {
    // 判斷表格是否存在宛琅,不存在就創(chuàng)建
    Class cls = [model class];
    if (![XTableModel isTableExists:cls uid:uid]) {
        NSLog(@"表不存在");
        [self createTable:cls uid:uid];
    }
    // 判斷是否需要更新, 需要,就更新
    if ([self isTableRequiredUpdate:cls uid:uid]) {
        BOOL result = [self isSuccessUpdateTable:cls uid:uid];
        if (!result) {
            NSLog(@"更新表格失敗");
            return NO;
        }
    }
    NSString *tableName = [XModelTool tableName:cls];
    // 獲取主鍵
    if (![cls respondsToSelector:@selector(primaryKey)]) {
        NSLog(@"請先實現(xiàn)+ primaryKey 方法");
        return nil;
    }
    NSString *primaryKey = [cls primaryKey];
    id primaryValue = [model valueForKeyPath:primaryKey];
    // 根據(jù)主鍵的值判斷是更新還是保存(有值-更新逗旁,無值-保存
    NSString *checkSql = [NSString stringWithFormat:@"select * from %@ where %@ = '%@'", tableName, primaryKey, primaryValue];
    NSArray *result = [XSqliteTool querySql:checkSql uid:uid];
    
    NSArray *columnNames = [XModelTool classIvarNameAndTypeDict:cls].allKeys;
    NSMutableArray *setValueArray = [NSMutableArray array];
    NSMutableArray *values = [NSMutableArray array];
    
    for (NSString *columnName in columnNames) {
        id value = [model valueForKeyPath:columnName];
        if ([value isKindOfClass:[NSArray class]] || [value isKindOfClass:[NSDictionary class]]) {
            NSData *data = [NSJSONSerialization dataWithJSONObject:value options:NSJSONWritingPrettyPrinted error:nil];
            value = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
        }
        
        [values addObject:value];
        
        NSString *str = [NSString stringWithFormat:@"%@='%@'", columnName, value];
        [setValueArray addObject:str];
    }
    
    NSString *execSql;
    // 更新
    if (result.count > 0) {
        execSql = [NSString stringWithFormat:@"update %@ set %@ where %@ = %@", tableName, [setValueArray componentsJoinedByString:@","], primaryKey, primaryValue];
    } else { // 插入
        execSql = [NSString stringWithFormat:@"insert into %@(%@) values('%@')", tableName, [columnNames componentsJoinedByString:@","], [values componentsJoinedByString:@"','"]];
    }
    return [XSqliteTool dealSql:execSql uid:uid];
}

刪除模型夯秃、查詢模型

  • 此處均根據(jù) 主鍵 進行刪除或查詢操作。也可以根據(jù)不同情況進行多個條件刪除或查詢痢艺,此處不做處理
#pragma mark - 通過操作模型 刪除數(shù)據(jù)
+ (BOOL)deleteModel:(id)model uid:(NSString *)uid {
    Class cls = [model class];
    NSString *tableName = [XModelTool tableName:cls];
    // 獲取主鍵
    if (![cls respondsToSelector:@selector(primaryKey)]) {
        NSLog(@"請先實現(xiàn)+ primaryKey 方法");
        return nil;
    }
    NSString *primaryKey = [cls primaryKey];
    id primaryValue = [model valueForKeyPath:primaryKey];
   
    NSString *execSql = [NSString stringWithFormat:@"delete from %@ where %@ = '%@'", tableName, primaryKey, primaryValue];
    return [XSqliteTool dealSql:execSql uid:uid];
}

#pragma mark -  通過操作模型 查找數(shù)據(jù)
+ (NSArray *)queryModel:(id)model uid:(NSString *)uid {
    Class cls = [model class];
    NSString *tableName = [XModelTool tableName:cls];
    // 獲取主鍵
    if (![cls respondsToSelector:@selector(primaryKey)]) {
        NSLog(@"請先實現(xiàn)+ primaryKey 方法");
        return nil;
    }
    NSString *primaryKey = [cls primaryKey];
    id primaryValue = [model valueForKeyPath:primaryKey];
    
    NSString *execSql = [NSString stringWithFormat:@"select * from %@ where %@ = '%@'", tableName, primaryKey, primaryValue];
    NSArray *resultArr = [XSqliteTool querySql:execSql uid:uid];
    return [self parseResults:resultArr withClass:cls];
}

+ (NSArray *)parseResults: (NSArray <NSDictionary *>*)results withClass:(Class)cls {
    NSDictionary *nameTypeDict = [XModelTool classIvarNameAndTypeDict:cls];
    
    NSMutableArray *models = [NSMutableArray array];
    for (NSDictionary *dict in results) {
        id model = [[cls alloc] init];
        [models addObject:model];
        [dict enumerateKeysAndObjectsUsingBlock:^(id  _Nonnull key, id  _Nonnull obj, BOOL * _Nonnull stop) {
            NSString *type = nameTypeDict[key];
            id resultValue = obj;
            if ([type isEqualToString:@"NSArray"] || [type isEqualToString:@"NSDictionary"]) {
                NSData *data = [obj dataUsingEncoding:NSUTF8StringEncoding];
                resultValue = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
            } else if ([type isEqualToString:@"NSMutableArray"] || [type isEqualToString:@"NSMutableDictionary"]) {
                NSData *data = [obj dataUsingEncoding:NSUTF8StringEncoding];
                resultValue = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil];
            }
            [model setValue:resultValue forKeyPath:key];
        }];
    }
    NSLog(@"%@", models);
    return models;
}

代碼請點擊 Demo

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末仓洼,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子堤舒,更是在濱河造成了極大的恐慌色建,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,729評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件舌缤,死亡現(xiàn)場離奇詭異箕戳,居然都是意外死亡,警方通過查閱死者的電腦和手機国撵,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,226評論 3 399
  • 文/潘曉璐 我一進店門陵吸,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人介牙,你說我怎么就攤上這事壮虫。” “怎么了环础?”我有些...
    開封第一講書人閱讀 169,461評論 0 362
  • 文/不壞的土叔 我叫張陵囚似,是天一觀的道長。 經(jīng)常有香客問我线得,道長饶唤,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,135評論 1 300
  • 正文 為了忘掉前任贯钩,我火速辦了婚禮募狂,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘角雷。我一直安慰自己祸穷,他們只是感情好,可當我...
    茶點故事閱讀 69,130評論 6 398
  • 文/花漫 我一把揭開白布谓罗。 她就那樣靜靜地躺著粱哼,像睡著了一般。 火紅的嫁衣襯著肌膚如雪檩咱。 梳的紋絲不亂的頭發(fā)上揭措,一...
    開封第一講書人閱讀 52,736評論 1 312
  • 那天胯舷,我揣著相機與錄音,去河邊找鬼绊含。 笑死桑嘶,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的躬充。 我是一名探鬼主播逃顶,決...
    沈念sama閱讀 41,179評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼充甚!你這毒婦竟也來了以政?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,124評論 0 277
  • 序言:老撾萬榮一對情侶失蹤伴找,失蹤者是張志新(化名)和其女友劉穎盈蛮,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體技矮,經(jīng)...
    沈念sama閱讀 46,657評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡抖誉,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,723評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了衰倦。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片袒炉。...
    茶點故事閱讀 40,872評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖樊零,靈堂內(nèi)的尸體忽然破棺而出我磁,到底是詐尸還是另有隱情,我是刑警寧澤淹接,帶...
    沈念sama閱讀 36,533評論 5 351
  • 正文 年R本政府宣布十性,位于F島的核電站叛溢,受9級特大地震影響塑悼,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜楷掉,卻給世界環(huán)境...
    茶點故事閱讀 42,213評論 3 336
  • 文/蒙蒙 一厢蒜、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧烹植,春花似錦斑鸦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,700評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至墩虹,卻和暖如春嘱巾,著一層夾襖步出監(jiān)牢的瞬間憨琳,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,819評論 1 274
  • 我被黑心中介騙來泰國打工旬昭, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留篙螟,地道東北人。 一個月前我還...
    沈念sama閱讀 49,304評論 3 379
  • 正文 我出身青樓问拘,卻偏偏與公主長得像遍略,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,876評論 2 361

推薦閱讀更多精彩內(nèi)容

  • 一雏掠、基本創(chuàng)建要做的的事情: 主要思想:想辦法湊夠拼接完整建表sql語句, 需要的數(shù)據(jù)勾扭。 表的名稱:根據(jù)類名定義 字...
    Lovell_閱讀 990評論 0 0
  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 1,732評論 0 9
  • 本節(jié)主要講 數(shù)組和字典的 存取寞忿,通過data和string的轉(zhuǎn)換來達到目的。 SqliteTool:執(zhí)行多條sql...
    Lovell_閱讀 1,085評論 4 1
  • 本文為轉(zhuǎn)載顶岸,原文:Vue學習筆記入門篇——數(shù)據(jù)及DOM 數(shù)據(jù) data 類型 Object | Function ...
    ChainZhang閱讀 369評論 0 3
  • 這幾天休息的時候看了命運石之門這部動漫腔彰,雖然我好幾年前就看過了,但是那個時候看其實沒有什么感覺辖佣,過了這么多年也幾乎...
    鈐魚擺擺閱讀 421評論 1 1