您的位置:首页 > 移动开发 > IOS开发

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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  iOS数据持久化 ios