上个版本为了增加用户体验,部分页面集成了离线缓存数据功能,于是就在项目里使用了数据库管理离线数据。下面交大家一步步学会使用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 用于直接调用。批量处理,使用事务。