SQLite封裝

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

  • 打開數據庫
#pragma mark - 打開數據庫
+ (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;
}
  • 關閉數據庫
#pragma mark - 關閉數據庫
+ (void)closeDB {
    sqlite3_close(ppDb);
}
  • 執(zhí)行語句
#pragma mark - 執(zhí)行數據庫
+ (BOOL)dealSql: (NSString *)sql uid: (NSString *)uid {
    if (![self openDB:uid]) {
        NSLog(@"打開數據庫失敗");
        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(@"打開數據庫失敗");
        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é)議,讓類遵守協(xié)議,實現忽略字段的方法

  • 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(@"請先實現+ 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 服務,實現了如下方法
    // 獲取表名
    + (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)的遷移數據
    1. 創(chuàng)建新的臨時表格
    2. 以新表為基準,從舊表中,取數據進行填充
    2.0 根據主鍵,插入主鍵的數據
    2.1 按照新表的有效字段(即是 舊表中包含的字段),從舊表中更新數據到新表
    3. 刪除舊表
    4. 修改臨時表格的名稱為新表

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

  • 主要代碼如下

#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)建正確結構的臨時表
    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(@"請先實現+ 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.插入舊表中的主鍵數據到臨時表
    NSString *insertPKeySql = [NSString stringWithFormat:@"insert into %@(%@) select %@ from %@", tempTableName, primaryKey, primaryKey, tableName];
    [sqls addObject:insertPKeySql];
    
    // 根據主鍵更新新表內容
    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í)行語句前,打開事務
     2. 如果有語句執(zhí)行結果失敗,則回滾事務
     3. 所有結果都成功,就提交事務 

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

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

  • 代碼

+ (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(@"請先實現+ primaryKey 方法");
        return nil;
    }
    NSString *primaryKey = [cls primaryKey];
    id primaryValue = [model valueForKeyPath:primaryKey];
    // 根據主鍵的值判斷是更新還是保存(有值-更新,無值-保存
    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];
}

刪除模型、查詢模型

  • 此處均根據 主鍵 進行刪除或查詢操作。也可以根據不同情況進行多個條件刪除或查詢,此處不做處理
#pragma mark - 通過操作模型 刪除數據
+ (BOOL)deleteModel:(id)model uid:(NSString *)uid {
    Class cls = [model class];
    NSString *tableName = [XModelTool tableName:cls];
    // 獲取主鍵
    if (![cls respondsToSelector:@selector(primaryKey)]) {
        NSLog(@"請先實現+ 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 -  通過操作模型 查找數據
+ (NSArray *)queryModel:(id)model uid:(NSString *)uid {
    Class cls = [model class];
    NSString *tableName = [XModelTool tableName:cls];
    // 獲取主鍵
    if (![cls respondsToSelector:@selector(primaryKey)]) {
        NSLog(@"請先實現+ 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

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

相關閱讀更多精彩內容

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

友情鏈接更多精彩內容