SQLite封裝

1. 創(chuàng)建 XSqliteTool 類,對(duì) SQLite 基本操作進(jìn)行封裝

  • 打開(kāi)數(shù)據(jù)庫(kù)
#pragma mark - 打開(kāi)數(shù)據(jù)庫(kù)
+ (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ù)庫(kù)
#pragma mark - 關(guān)閉數(shù)據(jù)庫(kù)
+ (void)closeDB {
    sqlite3_close(ppDb);
}
  • 執(zhí)行語(yǔ)句
#pragma mark - 執(zhí)行數(shù)據(jù)庫(kù)
+ (BOOL)dealSql: (NSString *)sql uid: (NSString *)uid {
    if (![self openDB:uid]) {
        NSLog(@"打開(kāi)數(shù)據(jù)庫(kù)失敗");
        return false;
    }
    BOOL result = sqlite3_exec(ppDb, sql.UTF8String, nil, nil, nil) == SQLITE_OK;
    [self closeDB];
    return result;
}
  • 查詢語(yǔ)句
#pragma mark -  查詢操作
+ (NSMutableArray<NSMutableDictionary *> *)querySql:(NSString *)sql uid:(NSString *)uid {
    if (![self openDB:uid]) {
        NSLog(@"打開(kāi)數(shù)據(jù)庫(kù)失敗");
        return false;
    }
    sqlite3_stmt *stmt = nil;
    if (sqlite3_prepare_v2(ppDb, sql.UTF8String, -1, &stmt, nil) != SQLITE_OK) {
        NSLog(@"準(zhǔn)備語(yǔ)句失敗");
    }
    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 類 動(dòng)態(tài)創(chuàng)建表

  • 基本創(chuàng)建概要
    - 拼接完整的sql語(yǔ)句
    - 表名:以類的名字定義
    - 字段名稱:類的成員變量
    - 主鍵:通過(guò)協(xié)議,讓類遵守協(xié)議,實(shí)現(xiàn)主鍵方法
    - 需要忽略的字段:通過(guò)協(xié)議,讓類遵守協(xié)議,實(shí)現(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(@"請(qǐng)先實(shí)現(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ù),實(shí)現(xiàn)了如下方法
    // 獲取表名
    + (NSString *)tableName: (Class)cls;
    // 獲取成員變量和成員變量的類型 字典
    + (NSDictionary *)classIvarNameAndTypeDict: (Class)cls;
    // 獲取類的成員變量和成員變量的類型映射成sqlite的類型 字典
    + (NSDictionary *)classIvarNameAndSqliteTypeDict: (Class)cls;
    // 獲取表的字段及類型
    + (NSString *)columnNameAndTypeStr: (Class)cls;
    // 所有排好序的表的字段
    + (NSArray *)tableSortedIvarNames: (Class)cls;

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

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

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

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

  • 主要代碼如下

#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語(yǔ)句
+ (NSArray *)udpateSqls: (Class)cls uid: (NSString *)uid {
    if ([self isTableRequiredUpdate:cls uid:uid] == NO) {
        NSLog(@"不需要更新表");
        return nil;
    }
    // 創(chuàng)建正確結(jié)構(gòu)的臨時(shí)表
    NSMutableArray *sqls = [NSMutableArray array];
    NSString *tableName = [XModelTool tableName:cls];
    // 1.創(chuàng)建臨時(shí)表
    NSString *tempTableName = [XModelTool tempTableName:cls];
    NSString *columnNameAndType = [XModelTool columnNameAndTypeStr:cls];
    
    if (![cls respondsToSelector:@selector(primaryKey)]) {
        NSLog(@"請(qǐng)先實(shí)現(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ù)到臨時(shí)表
    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;

多條語(yǔ)句的處理

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

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

  • 操作步驟
    1. 檢查表格是否存在,沒(méi)有則創(chuàng)建
    2. 檢查表格是否需要更新,需要?jiǎng)t更新
    3. 插入或者更新
    - 根據(jù)主鍵判斷記錄是否存在:
    - 存在,則拼接更新語(yǔ)句;不存在,則拼接插入語(yǔ)句

  • 代碼

+ (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(@"請(qǐng)先實(shí)現(xiàn)+ primaryKey 方法");
        return nil;
    }
    NSString *primaryKey = [cls primaryKey];
    id primaryValue = [model valueForKeyPath:primaryKey];
    // 根據(jù)主鍵的值判斷是更新還是保存(有值-更新,無(wú)值-保存
    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ìn)行刪除或查詢操作。也可以根據(jù)不同情況進(jìn)行多個(gè)條件刪除或查詢,此處不做處理
#pragma mark - 通過(guò)操作模型 刪除數(shù)據(jù)
+ (BOOL)deleteModel:(id)model uid:(NSString *)uid {
    Class cls = [model class];
    NSString *tableName = [XModelTool tableName:cls];
    // 獲取主鍵
    if (![cls respondsToSelector:@selector(primaryKey)]) {
        NSLog(@"請(qǐng)先實(shí)現(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 -  通過(guò)操作模型 查找數(shù)據(jù)
+ (NSArray *)queryModel:(id)model uid:(NSString *)uid {
    Class cls = [model class];
    NSString *tableName = [XModelTool tableName:cls];
    // 獲取主鍵
    if (![cls respondsToSelector:@selector(primaryKey)]) {
        NSLog(@"請(qǐng)先實(shí)現(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;
}

代碼請(qǐng)點(diǎn)擊 Demo

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容