上個(gè)版本為了增加用戶體驗(yàn),部分頁(yè)面集成了離線緩存數(shù)據(jù)功能,于是就在項(xiàng)目里使用了數(shù)據(jù)庫(kù)管理離線數(shù)據(jù)。下面交大家一步步學(xué)會(huì)使用FMDB,以及FMDB的二次封裝,同事把我二次封裝的數(shù)據(jù)庫(kù)放出來(lái),希望能夠幫助大家快速學(xué)習(xí),集成數(shù)據(jù)庫(kù)功能吧。
一.首先看一下STDB文件結(jié)構(gòu)
-
Table.h
主要放一些Table的創(chuàng)建語(yǔ)句, 方便管理我的數(shù)據(jù)庫(kù)各張表創(chuàng)建 -
DBDefine.h
主要放一些表名的宏定義,數(shù)據(jù)庫(kù)版本號(hào),數(shù)據(jù)庫(kù)名字等等,方便我們?cè)谑褂脭?shù)據(jù)庫(kù)過(guò)程中更直觀管理版本和各種表 -
STDBTool.h,STDBTool.m
具體封裝實(shí)現(xiàn)代碼

二.具體實(shí)現(xiàn)功能
1 . STDBTool.h
頭文件看一下
我定義了三個(gè)FMDatabaseQueue 因?yàn)閷?shí)際操作中我需要數(shù)據(jù)庫(kù)嵌套,如果只使用一個(gè)FMDatabaseQueue 將會(huì)陷入死循環(huán),下面看一下1
FMDatabaseQueue
源碼分析一下原因:
_queue = dispatch_queue_create([[NSString stringWithFormat:@"fmdb.%@", self] UTF8String], NULL);
默認(rèn)是串行隊(duì)列,數(shù)據(jù)庫(kù)操作的時(shí)候FMDB源碼如下圖
同步執(zhí)行串行隊(duì)列 block塊里按著順序執(zhí)行。

任務(wù)1執(zhí)行 ——>任務(wù)二等待任務(wù)一執(zhí)行完畢執(zhí)行,任務(wù)一等待任務(wù)二執(zhí)行完畢執(zhí)行,死鎖。 如果新建一個(gè)串行隊(duì)列

這樣就沒(méi)有問(wèn)題。 至于同步異步并行串行網(wǎng)上也有很多,不在一一介紹啦。
2 . STDBTool的初始化 ,很顯然 STDBTool用的是單例啦, 看一下alloc方法
這里面也就是創(chuàng)建一下數(shù)據(jù)庫(kù)文件,設(shè)置數(shù)據(jù)庫(kù)版本號(hào)。
當(dāng)數(shù)據(jù)庫(kù)有新表需要增加時(shí),我們需要改變數(shù)據(jù)庫(kù)版本號(hào),對(duì)數(shù)據(jù)庫(kù)進(jìn)行升級(jí)。
- .查詢數(shù)據(jù)庫(kù)當(dāng)前版本
NSString * sql = [NSString stringWithFormat:@"PRAGMA user_version"];
FMResultSet * rs = [db executeQuery:sql];
int nVersion = 0;
while ([rs next]) {
nVersion = [rs intForColumn:@"user_version"];
}
- 與宏定義數(shù)據(jù)庫(kù)版本不一致 需要更新 設(shè)置 數(shù)據(jù)庫(kù)版本號(hào)
NSString *sql = [NSString stringWithFormat:@"PRAGMA user_version = %ld",(long)newVersion];
BOOL ret = [db executeUpdate:sql];
STDBTool初始化就這些吧。也沒(méi)什么難點(diǎn),主要是跟大家一起回顧一下。
3 .實(shí)現(xiàn)的數(shù)據(jù)庫(kù)數(shù)據(jù)操作功能
一般數(shù)據(jù)庫(kù)操作無(wú)非是增刪改查這些基本操作,當(dāng)然我的封裝也是基于實(shí)現(xiàn)這些功能的。但是根據(jù)具體情況我們需要進(jìn)行封裝。
- 執(zhí)行單個(gè)sql語(yǔ)句時(shí)候,不需要使用事務(wù)處理,我們需要知道操作類型,這里我寫了個(gè)枚舉type 便于區(qū)分
-(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) {
//查詢語(yǔ)句 需要返回記錄集
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{
//更新操作 只關(guān)心操作是否執(zhí)行成功,不關(guān)心記錄集 返回布爾值 無(wú)執(zhí)行結(jié)果
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);
}
}
}];
}
- 根據(jù)查詢結(jié)果 確定是更新還是新增操作,只需要知道是否操作成功,不關(guān)心結(jié)果集 只處理一個(gè)查詢更新,不需要事務(wù)處理
- (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]) {
//獲取查詢數(shù)據(jù)的個(gè)數(shù)
nCount = [rs intForColumnIndex:0];
}
[rs close];
NSString * nextSqlString = nil;
if (nCount > 0) {
//查詢到了結(jié)果 執(zhí)行update操作
nextSqlString = sqlList[1];
}else{
//查詢無(wú)結(jié)果 執(zhí)行 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語(yǔ)句數(shù)組,sqlList[0]
查詢select語(yǔ)句 sqList[1]
update更新語(yǔ)句 sqlList[2]
insert into 插入語(yǔ)句
- 根據(jù)查詢結(jié)果 確定是更新還是新增操作,只需要知道是否操作成功,不關(guān)心結(jié)果集 只處理一個(gè)查詢更新,不需要事務(wù)處理
- (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]) {
//獲取查詢數(shù)據(jù)的個(gè)數(shù)
nCount = [rs intForColumnIndex:0];
}
[rs close];
NSString * nextSqlString = nil;
if (nCount > 0) {
//查詢到了結(jié)果 執(zhí)行update操作
nextSqlString = sqlList[1];
}else{
//查詢無(wú)結(jié)果 執(zhí)行 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語(yǔ)句數(shù)組,sqlList[0]查詢select語(yǔ)句 sqList1update更新語(yǔ)句 sqlList2 insert into 插入語(yǔ)句
4 . sqlList 是一個(gè)二維數(shù)組,每一個(gè)成員包含三個(gè)sql語(yǔ)句,分別是查詢,更新,插入,并且根據(jù)查詢結(jié)果返回是執(zhí)行更新 還是 插入操作。使用dbQueue2 用于直接調(diào)用。批量處理,使用事務(wù)。
- (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){
//執(zhí)行更新
nextSqlString = singleSqlList[1];
}
else{
//執(zhí)行插入
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語(yǔ)句數(shù)組,sqlArr[i][0]:查詢語(yǔ)句;sqlArri:update語(yǔ)句;sqlArri:insert into語(yǔ)句
5.sql語(yǔ)句數(shù)組中每個(gè)成員有2條語(yǔ)句,第一條是select語(yǔ)句,第二條是insert into語(yǔ)句,根據(jù)第一個(gè)sql的執(zhí)行結(jié)果確定第二條語(yǔ)句是否執(zhí)行。根據(jù)查詢結(jié)果確定是否新增,批量處理,使用事務(wù)處理,不需要返回記錄集使用dbQueue2,用于程序中直接調(diào)用(非封裝在其他方法中)
-(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語(yǔ)句數(shù)組,sqlArr[i][0]:查詢語(yǔ)句;sqlArri:insert into語(yǔ)句
6.批量處理更新或者新增sql語(yǔ)句,不需要返回記錄集 無(wú)事務(wù)處理
- (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語(yǔ)句數(shù)組update或者insert into語(yǔ)句
7.批量處理更新或者新增sql語(yǔ)句,并且不需要返回記錄集,使用事務(wù)處理
-(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語(yǔ)句數(shù)組update或者insert into語(yǔ)句
還有幾個(gè)方法在防止死循環(huán)嵌套 類似的函數(shù)。
github地址可以直接下載使用,感覺(jué)有用的話star一下,謝謝大家。希望能幫到大家。