FMDB
2016-02-25 14:59
645 查看
FMDB的使用介绍
iOS中原生的SQLite API在使用上相当不友好,在使用时,非常不便。于是,就出现了一系列将SQLite API进行封装的库,FMDB (https://github.com/ccgus/fmdb) 是一款简洁、易用的封装库,这一篇文章简单介绍下FMDB的使用。在FMDB下载文件后,工程中必须导入如下文件,并使用 libsqlite3.dylib 依赖包。
MRC和ARC
在FMDB中无论使用ARC还是MRC都没有任何影响,FMDB在编译项目时自动匹配。
如何使用
FMDB有三个主要的类1.FMDatabase – 表示一个单独的SQLite数据库。 用来执行SQLite的命令。 2.FMResultSet – 表示FMDatabase执行查询后结果集 3.FMDatabaseQueue – 如果你想在多线程中执行多个查询或更新,你应该使用该类。这是线程安全的。
FMDatabase *db = [FMDatabase databaseWithPath:@"/tmp/tmp.db"];
打开数据库
在使用数据库之前必须先打开,如果资源或者权限较低都可能会导致打开失败
if (![db open]) { [db release]; return; }
执行更新
一切不是SELECT命令的命令都被视为更新,这包括CREATE, UPDATE, INSERT, ALTER, COMMIT, BEGIN, DETACH, DELETE, DROP, END, EXPLAIN, VACUUM, and REPLACE 简单来说,只要不是SELECT开头的命令都是update命令。 执行更新返回一个BOOL值。YES表示成功,否则失败。你可以调用 -lastErrorMessage和-lastErrorCode方法来的到更多的信息。
执行查询
SELECT命令就是查询,执行查询的方法是以-excuteQuery开头的。 执行查询时,如果成功返回FMResultSet对象, 错误返回nil. 与执行更新相当,支持使用 NSError**参数。同时,你也可以使用 -lastErrorCode和-lastErrorMessage获知错误信息。 为了遍历查询结果,你可以使用while循环。你还需要知道怎么跳到下一个记录。使用FMDB,很简单实现,就像这样:
FMResultSet *s = [db executeQuery:@"SELECT * FROM myTable"]; while ([s next]) { //retrieve values for each record }
FMResultSet 提供了很多方法来获得所需的格式的值:
intForColumn: longForColumn: longLongIntForColumn: boolForColumn: doubleForColumn: stringForColumn: dataForColumn: dataNoCopyForColumn: UTF8StringForColumnIndex: objectForColumn:
关闭数据库
当时用完数据库时,你应该调用-close来关闭数据库连接来释放SQLite使用的资源。
[db close];
事务
FMDatabase可以开始和提交一个事务通过回调 使用FMDB,插入数据前,你不要花时间审查你的数据。你可以使用标准的SQLite数据绑定语法。
INSERT INTO myTable VALUES (?, ?, ?)
SQLite会识别 “?” 为一个输入的点位符, 这样的执行会接受一个可变参数(或者表示为其他参数,如NSArray, NSDictionary,或va_list等),会正确为您转义。 提供给 -executeUpdate: 方法的参数都必须是对象。就像以下的代码就无法工作,且会产生崩溃。
[db executeUpdate:@"INSERT INTO myTable VALUES (?)", 42];
正确有做法是把数字打包成 NSNumber对象
[db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:42]];
或者,你可以使用 -execute*WithFormat: ,这是NSString风格的参数
[db executeUpdateWithFormat:@"INSERT INTO myTable VALUES (%d)", 42];
-execute*WithFormat: 的方法的内部实现会帮你封装数据, 以下这些修饰符都可以使用: %@, %c, %s, %d, %D,%i, %u, %U, %hi, %hu, %qi, %qu, %f, %g, %ld, %lu, %lld, and %llu. 除此之外的修饰符可能导致无法预知的结果。 一些情况下,你需要在SQL语句中使用 % 字符,你应该使用 %%。
使用FMDatabaseQueue及线程安全
在多个线程中同时使用一个FMDatabase实例是不明智的。现在你可以为每个线程创建一个FMDatabase对象。 不要让多个线程分享同一个实例,它无法在多个线程中同时使用。 若此,坏事会经常发生,程序会时不时崩溃,或者报告异常,或者陨石会从天空中掉下来砸到你Mac Pro. 总之很崩溃。所以,不要初始化FMDatabase对象,然后在多个线程中使用。请使用 FMDatabaseQueue,它是你的朋友而且会帮助你。以下是使用方法:
FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithPath:aPath]; [queue inDatabase:^(FMDatabase *db) { [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:1]]; [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:2]]; [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:3]]; FMResultSet *rs = [db executeQuery:@"select * from foo"]; while([rs next]) { … } }];
FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithPath:aPath]; [queue inTransaction:^(FMDatabase *db, BOOL *rollback) { [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:1]]; [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:2]]; [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:3]]; if (whoopsSomethingWrongHappened) { *rollback = YES; return; } // etc… [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:4]]; }];
实例
一个简单的诗词项目TRDBManager.h
#import <Foundation/Foundation.h> #import "FMDatabase.h" @interface TRDBManager : NSObject //单例模式,返回唯一的数据库对象 + (FMDatabase *)sharedDatabase; @end
TRDBManager.m
#import "TRDBManager.h" @implementation TRDBManager + (FMDatabase *)sharedDatabase { static FMDatabase *database = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ //拼接拷贝的路径(/Documents/sqlite.db) NSString *documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject]; NSString *copyDBPath = [documentsPath stringByAppendingPathComponent:@"sqlite.db"]; //获取数据文件的路径 NSString *bundlePath = [[NSBundle mainBundle] pathForResource:@"Poetry" ofType:@"bundle"]; //拼接数据库文件的路径 NSString *databaseFilePath = [bundlePath stringByAppendingPathComponent:@"sqlite.db"]; NSError *error = nil; if (![[NSFileManager defaultManager] fileExistsAtPath:copyDBPath]) { [[NSFileManager defaultManager] copyItemAtPath:databaseFilePath toPath:copyDBPath error:&error]; } if (!error) { //初始化database对象 database = [FMDatabase databaseWithPath:copyDBPath]; } else { NSLog(@"拷贝失败:%@", error.userInfo); } }); //在使用对象之前,要打开数据库。这样返回的话那么使用的时候就不需要这步操作了 [database open]; return database; } @end
TRPoetryKind.h
#import <Foundation/Foundation.h> @interface TRPetryKind : NSObject @property(nonatomic,strong) NSString *kind; @property(nonatomic) long num; @property(nonatomic,strong) NSString *introKind; @property(nonatomic,strong) NSString *introKind2; //删除当前数据 + (BOOL)removeKind:(NSString *)kindStr; //获取当前所有的分类 + (NSArray *)kinds; @end
TRPoetryKind.m
#import "TRPetryKind.h" #import "FMDatabase.h" #import "TRDBManager.h" @implementation TRPetryKind + (BOOL)removeKind:(NSString *)kindStr { FMDatabase *db = [TRDBManager sharedDatabase]; BOOL success = [db executeUpdateWithFormat:@"delete from t_kind where D_KIND = %@", kindStr]; [db close]; return success; } + (NSArray *)kinds { //从数据库中获取t_kind表中所有数据 //获取数据库单例对象 FMDatabase *db = [TRDBManager sharedDatabase]; //查询所有的分类 FMResultSet *rs = [db executeQuery:@"select * from T_KIND"]; //可变数组用来存放分类模型 NSMutableArray *dataArr = [NSMutableArray new]; while ([rs next]) { TRPetryKind *model = [self new]; model.kind = [rs stringForColumn:@"D_KIND"]; model.num = [rs longForColumn:@"D_NUM"]; model.introKind = [rs stringForColumn:@"D_INTROKIND"]; model.introKind2 = [rs stringForColumn:@"D_INTROKIND2"]; [dataArr addObject:model]; } //释放掉搜索出来的内容 [db closeOpenResultSets]; [db close]; return [dataArr copy]; } @end
TRPoetry.h
#import <Foundation/Foundation.h> @interface TRPoetry : NSObject @property(nonatomic,strong) NSString *kind; @property(nonatomic,strong) NSString *shi; @property(nonatomic,strong) NSString *introShi; @property(nonatomic,strong) NSString *title; @property(nonatomic) long ID; @property(nonatomic,strong) NSString *author; //根据传入的诗词的ID 删除该诗 + (BOOL)removePoetry:(long)poemID; //获取某类诗词的所有列表 + (NSArray *)poetryListWithKind:(NSString *)kind; @end
TRPoetry.m
#import "TRPoetry.h" #import "FMDatabase.h" #import "TRDBManager.h" @implementation TRPoetry //把搜索结果FMResultSet类型转换为 包含PoetryModel的数组类型 + (NSArray *)resultSetToPoetryList:(FMResultSet *)rs{ NSMutableArray *dataArr = [NSMutableArray new]; while ([rs next]) { TRPoetry *model = [self new]; model.kind = [rs stringForColumn:@"d_kind"]; model.author=[rs stringForColumn:@"d_author"]; model.title=[rs stringForColumn:@"d_title"]; model.ID = [rs longForColumn:@"d_id"]; model.shi = [rs stringForColumn:@"d_shi"]; model.introShi=[rs stringForColumn:@"d_introshi"]; [dataArr addObject:model]; } return [dataArr copy]; } + (NSArray *)poetryListWithSearchStr:(NSString *)searchStr{ FMDatabase *database = [TRDBManager sharedDatabase]; //SQL语句 通配符 ss -> %ss% //如果要在format中输入%,需要转义符 %配合 searchStr = [NSString stringWithFormat:@"%%%@%%", searchStr]; FMResultSet *resultSet = [database executeQueryWithFormat:@"select * from t_shi where d_title like %@ or d_author like %@", searchStr, searchStr]; NSArray *array = [self resultSetToPoetryList:resultSet]; //操作完了管你数据库 [database close]; return array; } + (BOOL)removePoetry:(long)poemID { FMDatabase *database = [TRDBManager sharedDatabase]; BOOL success = [database executeUpdateWithFormat:@"delete from t_shi where d_id = %ld", poemID]; [database close]; return success; } + (NSArray *)poetryListWithKind:(NSString *)kind{ FMDatabase *database = [TRDBManager sharedDatabase]; //如果数据库语句需要传参 FMResultSet *resultSet = [database executeQueryWithFormat:@"select * from T_SHI where d_kind = %@", kind]; NSArray *array = [self resultSetToPoetryList:resultSet]; //关闭数据库 [database close]; return array; } @end
相关文章推荐
- 峰回路转,Firefox 浏览器即将重返 iOS 平台
- 峰回路转,Firefox 浏览器即将重返 iOS 平台
- 不可修补的 iOS 漏洞可能导致 iPhone 4s 到 iPhone X 永久越狱
- iOS 12.4 系统遭黑客破解,漏洞危及数百万用户
- 每日安全资讯:NSO,一家专业入侵 iPhone 的神秘公司
- [转][源代码]Comex公布JailbreakMe 3.0源代码
- 讲解iOS开发中基本的定位功能实现
- js判断客户端是iOS还是Android等移动终端的方法
- iOS应用中UISearchDisplayController搜索效果的用法
- IOS开发环境windows化攻略
- 浅析iOS应用开发中线程间的通信与线程安全问题
- 检测iOS设备是否越狱的方法
- .net平台推送ios消息的实现方法
- 探讨Android与iOS,我们将何去何从?
- Android、iOS和Windows Phone中的推送技术详解
- iOS推送的那些事
- IOS 改变键盘颜色代码
- 微信支付终于成功了(安卓、iOS)在此分享
- 举例详解iOS开发过程中的沙盒机制与文件