上個版本為了增加使用者體驗,部分頁面整合了離線快取資料功能,於是就在專案裡使用了資料庫管理離線資料。下面交大家一步步學會使用FMDB,以及FMDB的二次封裝,同事把我二次封裝的資料庫放出來,希望能夠幫助大家快速學習,整合資料庫功能吧。
一.首先看一下STDB檔案結構
Table.h
主要放一些Table的建立語句, 方便管理我的資料庫各張表建立DBDefine.h
主要放一些表名的巨集定義,資料庫版本號,資料庫名字等等,方便我們在使用資料庫過程中更直觀管理版本和各種表STDBTool.h,STDBTool.m
具體封裝實現程式碼
二.具體實現功能
1 . STDBTool.h
標頭檔案看一下
我定義了三個FMDatabaseQueue 因為實際操作中我需要資料庫巢狀,如果只使用一個FMDatabaseQueue 將會陷入死迴圈,下面看一下1FMDatabaseQueue
原始碼分析一下原因:
建立一個佇列_queue = dispatch_queue_create([[NSString stringWithFormat:@"fmdb.%@", self] UTF8String], NULL);
預設是序列佇列,資料庫操作的時候FMDB原始碼如下圖
同步執行序列佇列 block塊裡按著順序執行。
任務1執行 ——>任務二等待任務一執行完畢執行,任務一等待任務二執行完畢執行,死鎖。 如果新建一個序列佇列
這樣就沒有問題。 至於同步非同步並行序列網上也有很多,不在一一介紹啦。
2 . STDBTool的初始化 ,很顯然 STDBTool用的是單例啦, 看一下alloc方法
這裡面也就是建立一下資料庫檔案,設定資料庫版本號。
當資料庫有新表需要增加時,我們需要改變資料庫版本號,對資料庫進行升級。
- .查詢資料庫當前版本
1 2 3 4 5 6 |
NSString * sql = [NSString stringWithFormat:@"PRAGMA user_version"]; FMResultSet * rs = [db executeQuery:sql]; int nVersion = 0; while ([rs next]) { nVersion = [rs intForColumn:@"user_version"]; } |
- 與巨集定義資料庫版本不一致 需要更新 設定 資料庫版本號
12NSString *sql = [NSString stringWithFormat:@"PRAGMA user_version = %ld",(long)newVersion];BOOL ret = [db executeUpdate:sql];
STDBTool初始化就這些吧。也沒什麼難點,主要是跟大家一起回顧一下。3 .實現的資料庫資料操作功能
一般資料庫操作無非是增刪改查這些基本操作,當然我的封裝也是基於實現這些功能的。但是根據具體情況我們需要進行封裝。
- 執行單個sql語句時候,不需要使用事務處理,我們需要知道操作型別,這裡我寫了個列舉type 便於區分
1234567891011121314151617181920212223-(void)executeSQL:(NSString *)sqlStr actionType:(ST_DB_ActionType)actionType withBlock:(void(^)(BOOL bRet, FMResultSet *rs, NSString *msg))block{[_dbQueue inDatabase:^(FMDatabase *db) {if (actionType == ST_DB_SELECT) {//查詢語句 需要返回記錄集FMResultSet * rs = [db executeQuery:sqlStr];if ([db hadError]) {block(NO,rs,[db lastErrorMessage]);NSLog(@"executeSQL error %d: %@",[db lastErrorCode],[db lastErrorMessage]);}else{block(YES,rs,nil);}}else{//更新操作 只關心操作是否執行成功,不關心記錄集 返回布林值 無執行結果BOOL ret = [db executeUpdate:sqlStr];if ([db hadError]) {block(NO,nil,[db lastErrorMessage]);NSLog(@"executeSQL error %d: %@",[db lastErrorCode],[db lastErrorMessage]);}else{block(ret,nil,nil);}}}];}
- 根據查詢結果 確定是更新還是新增操作,只需要知道是否操作成功,不關心結果集 只處理一個查詢更新,不需要事務處理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
- (void)executeRelevanceSql:(NSArray *)sqlList withBlock:(void(^)(BOOL ret,NSString * errMsg))block{ __block BOOL ret; [_dbQueue inDatabase:^(FMDatabase *db) { FMResultSet * rs = [db executeQuery:sqlList[0]]; if ([db hadError]) { block(NO,[db lastErrorMessage]); NSLog(@"da_error_%@",[db lastErrorMessage]); } int nCount = 0; if ([rs next]) { //獲取查詢資料的個數 nCount = [rs intForColumnIndex:0]; } [rs close]; NSString * nextSqlString = nil; if (nCount > 0) { //查詢到了結果 執行update操作 nextSqlString = sqlList[1]; }else{ //查詢無結果 執行 insert into 操作 nextSqlString = sqlList[2]; } ret = [db executeUpdate:nextSqlString]; if ([db hadError]) { block(NO,[db lastErrorMessage]); NSLog(@"da_error_%@",[db lastErrorMessage]); }else{ block(ret, nil); } }]; } |
注:sql語句陣列,sqlList[0]
查詢select語句 sqList[1]
update更新語句 sqlList[2]
insert into 插入語句
- 根據查詢結果 確定是更新還是新增操作,只需要知道是否操作成功,不關心結果集 只處理一個查詢更新,不需要事務處理
1234567891011121314151617181920212223242526272829303132- (void)executeRelevanceSql:(NSArray *)sqlList withBlock:(void(^)(BOOL ret,NSString * errMsg))block{__block BOOL ret;[_dbQueue inDatabase:^(FMDatabase *db) {FMResultSet * rs = [db executeQuery:sqlList[0]];if ([db hadError]) {block(NO,[db lastErrorMessage]);NSLog(@"da_error_%@",[db lastErrorMessage]);}int nCount = 0;if ([rs next]) {//獲取查詢資料的個數nCount = [rs intForColumnIndex:0];}[rs close];NSString * nextSqlString = nil;if (nCount > 0) {//查詢到了結果 執行update操作nextSqlString = sqlList[1];}else{//查詢無結果 執行 insert into 操作nextSqlString = sqlList[2];}ret = [db executeUpdate:nextSqlString];if ([db hadError]) {block(NO,[db lastErrorMessage]);NSLog(@"da_error_%@",[db lastErrorMessage]);}else{block(ret, nil);}}];}
注: sql語句陣列,sqlList[0]查詢select語句 sqList1update更新語句 sqlList2 insert into 插入語句
4 . sqlList 是一個二維陣列,每一個成員包含三個sql語句,分別是查詢,更新,插入,並且根據查詢結果返回是執行更新 還是 插入操作。使用dbQueue2 用於直接呼叫。批量處理,使用事務。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
- (void)executeDbQueue2RelevanceTransactionSqlList:(NSArray *)sqlList withBlock:(void(^)(BOOL bRet, NSString *msg, BOOL *bRollback))block{ __block BOOL ret = NO; [_dbQueue2 inTransaction:^(FMDatabase *db, BOOL *rollback) { for (NSArray * singleSqlList in sqlList ) { FMResultSet * rs = [db executeQuery:singleSqlList[0]]; if ([db hadError]) { block(NO,[db lastErrorMessage],rollback); NSLog(@"da_error_%@",[db lastErrorMessage]); }else{ int nCount = 0; while ([rs next]){ nCount = [rs intForColumnIndex:0]; } [rs close]; NSString * nextSqlString = nil; if (nCount > 0){ //執行更新 nextSqlString = singleSqlList[1]; } else{ //執行插入 nextSqlString = singleSqlList[2]; } ret = [db executeUpdate:nextSqlString]; if ([db hadError]) { block(NO, [db lastErrorMessage], rollback); NSLog(@"executeSql Err %d: %@", [db lastErrorCode], [db lastErrorMessage]); } } } block(ret, nil, rollback); }]; } |
注:sql語句陣列,sqlArr[i][0]:查詢語句;sqlArri:update語句;sqlArri:insert into語句
5.sql語句陣列中每個成員有2條語句,第一條是select語句,第二條是insert into語句,根據第一個sql的執行結果確定第二條語句是否執行。根據查詢結果確定是否新增,批量處理,使用事務處理,不需要返回記錄集使用dbQueue2,用於程式中直接呼叫(非封裝在其他方法中)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
-(void)executeInsertTransactionSqlList:(NSArray *)sqlStrList withBlock:(void(^)(BOOL bRet, NSString *msg, BOOL *bRollback))block { __block BOOL ret = NO; NSLog(@"開始啦---"); [_dbQueue2 inTransaction:^(FMDatabase *db, BOOL *rollback){ for (NSArray *sqlArray in sqlStrList){ FMResultSet *rs = [db executeQuery:[sqlArray objectAtIndex:0]]; if ([db hadError]){ block(NO, [db lastErrorMessage], rollback); NSLog(@"executeSql Err %d: %@", [db lastErrorCode], [db lastErrorMessage]); } int nCount = 0; while ([rs next]){ nCount = [rs intForColumnIndex:0]; } [rs close]; if (nCount <= 0){ ret = [db executeUpdate:[sqlArray objectAtIndex:1]]; if ([db hadError]) { block(NO, [db lastErrorMessage], rollback); NSLog(@"executeSql Err %d: %@", [db lastErrorCode], [db lastErrorMessage]); } } } block(ret, nil, rollback); }]; } |
注:sql語句陣列,sqlArr[i][0]:查詢語句;sqlArri:insert into語句
6.批量處理更新或者新增sql語句,不需要返回記錄集 無事務處理
1 2 3 4 5 6 7 8 9 10 11 12 |
- (void)executeSQLList:(NSArray *)sqlStrList db:(FMDatabase *)db withBlock:(void(^)(BOOL bRet, NSString *msg))block{ __block BOOL bRet = NO; for (NSString * sqlString in sqlStrList) { bRet = [db executeUpdate:sqlString]; if ([db hadError]) { block(bRet,[db lastErrorMessage]); NSLog(@"executeSQLList Err %d: %@", [db lastErrorCode], [db lastErrorMessage]); break; } } block(bRet,nil); } |
注: sql語句陣列update或者insert into語句
7.批量處理更新或者新增sql語句,並且不需要返回記錄集,使用事務處理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
-(void)executeTransactionSqlList:(NSArray *)sqlStrList withBlock:(void(^)(BOOL bRet, NSString *msg, BOOL *bRollback))block { __block BOOL bRet = NO; [_dbQueue inTransaction:^(FMDatabase *db, BOOL *rollback){ for (NSString *sqlStr in sqlStrList) { bRet = [db executeUpdate:sqlStr]; if ([db hadError]) { block(bRet, [db lastErrorMessage], rollback); NSLog(@"executeSQLList Err %d: %@", [db lastErrorCode], [db lastErrorMessage]); break; } } block(bRet, nil, rollback); }]; } |
注:sql語句陣列update或者insert into語句
還有幾個方法在防止死迴圈巢狀 類似的函式。
github地址可以直接下載使用,感覺有用的話star一下,謝謝大家。希望能幫到大家。