这篇博客主要是用来简单介绍一下FMDB的基础用法,涉及到简单的增删改查
一:FMDB介绍
FMDB是一种第三方的开源库,FMDB就是对SQLite的API进行了封装,加上了面向对象的思想,简单来说就是让我们能更方遍的操作SQLite更加方便。
FMDB优点:
- 使用起来更加面向对象,省去了很多麻烦、冗余的C语言代码
- 对比苹果自带的CoreData框架,更加轻量级和灵活
- 提供多线程安全,有效地防止数据混乱,原来的SQLite不是线程安全的
使用FMDB需要的步骤:
- 项目中添加
libsqlite3
库的依赖 - 推荐直接使用
cocoapods
导入到项目当中 - 在需要用到的类中导入FMDB的头文件
#import "FMDatabase.h"
二:FMDB的使用
FMDB的具体步骤我简单的归类为一下几个小点,下面会一一描述相应的citydb
就是城市的数据库:
- 创建数据库
- 打开数据库
- 创建相应的库表
- 对库表进行操作
- 对查出数据的解析
- 关闭数据库
1:创建数据库的方法
根据FMDB官方的注解翻译过来的意思就是
/*
1. 这个文件不一定必须存在沙盒中,所以如果已经存在的话就直接使用,不存在就会帮你创建一个;
2. 你可以选择直接传空的字符串,那么系统会在临时目录创建一个空的数据库,当数据库关闭时,该数据库文件也被删除;
3. 如果传nil,会在内存中临时创建一个空的数据库,当数据库关闭时,数据库文件也被删除;
*/
+ (FMDatabase *)databaseWithPath:(NSString *)filePath;
相应的实现方法是:
NSString *DocumentPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
NSString *dbPath = [DocumentPath stringByAppendingPathComponent:@"data.db"];
FMDatabase *citydb = [FMDatabase databaseWithPath:dbPath];
2:打开数据库
/*
打开数据库,一般用法是在查询语句之前调用一下,返回值是BOOL类型,这个BOOL的意思是告诉你这次打开是否成功,然后打开成功以后你就能对你的数据库进行操作了
*/
[citydb open];
3:创建库表
这里额外说明一下,[citydb executeUpdate:sql];
这句话用的是executeUpdate
方法,因为添加库表可以理解为更新数据库的行为,所以executeUpdate
是可行的,同时用executeStatements
方法也是同样可行的,executeStatements
简单理解就是让数据库运行这个方法里的行为。
FMDatabase * db = [FMDatabase databaseWithPath:self.dbPath];
if ([db open]) {
NSString *sql = @"create table if not exists city(cityName text, cityId text, userId text);";
[citydb executeUpdate:sql];
}
4:对数据库进行操作
/* 执行更新的SQL语句,字符串里面的"?",依次用后面的参数替代,必须是对象,不能是int等基本类型 */
- (BOOL)executeUpdate:(NSString *)sql,... ;
/* 执行更新的SQL语句,可以使用字符串的格式化进行构建SQL语句 */
- (BOOL)executeUpdateWithFormat:(NSString*)format,... ;
/* 执行更新的SQL语句,字符串中有"?",依次用arguments的元素替代 */
- (BOOL)executeUpdate:(NSString*)sql withArgumentsInArray:(NSArray *)arguments;
下面我在列举一下简单的增删改查四个方法
增加字段到city
表
FMDatabase * db = [FMDatabase databaseWithPath:self.dbPath];
if ([db open]) {
BOOL res = NO;
NSDictionary *cityDic = @{@"杭州":@"1",@"上海":@"2",@"宁波":@"3",@"温州":@"4",@"台州":@"5",@"湖州":@"6",@"绍兴":@"7",@"金华":@"8"};
for (NSString *key in cityDic.allKeys) {
NSString *sqlStr = [NSString stringWithFormat:@"insert into city(cityName,cityId,userId)values('%@','%@','%@')",key,cityDic[key],@"123"];
res = [db executeUpdate:sqlStr];
}
if (!res) {
NSLog(@"error to insert city");
} else {
NSLog(@"success to insert city");
}
[db close];
}
删除city
表中,cityName
为“杭州” 且cityId
为“1”的字段
FMDatabase *db = [FMDatabase databaseWithPath:self.dbPath];
if ([db open]) {
NSString *sql = [NSString stringWithFormat:@"delete from city where cityName = '%@' and cityId = '%@'",@"杭州",@"1"];
BOOL rs = [db executeUpdate:sql];
if (!rs) {
NSLog(@"error to update city");
} else {
NSLog(@"success to update city");
}
[db close];
}
修改city
表中,cityId
为“2”的字段
FMDatabase * db = [FMDatabase databaseWithPath:self.dbPath];
if ([db open]) {
NSString *sqlStr = [NSString stringWithFormat:@"update city set cityName = '%@' where cityId = '2'",self.textField.text];
BOOL res = [db executeUpdate:sqlStr];
if (!res) {
NSLog(@"error to insert city");
} else {
NSLog(@"success to insert city");
}
[db close];
}
查询city
表中的所有字段
FMDatabase *db = [FMDatabase databaseWithPath:self.dbPath];
if ([db open]) {
NSMutableDictionary *mutableDic = [[NSMutableDictionary alloc] init];
NSString *sql = [NSString stringWithFormat:@"select * from city where userId = '%@'",@"123"];
FMResultSet *rs = [db executeQuery:sql];
while ([rs next]) {
NSString *name = [rs stringForColumn:@"cityName"];
NSString *cityId = [rs stringForColumn:@"cityId"];
[mutableDic setObject:cityId forKey:name];
}
[db close];
NSLog(@"%@",mutableDic);
}
5:对查出的数据的解析
在得到查出的数据以后,需要对数据进行一些对应的解析,根据数据的类型要分别处理
/* 获取下一个记录 */
- (BOOL)next;
/* 获取记录有多少列 */
- (int)columnCount;
/* 通过列名得到列序号,通过列序号得到列名 */
- (int)columnIndexForName:(NSString *)columnName;
- (NSString *)columnNameForIndex:(int)columnIdx;
/* 获取存储的整形值 */
- (int)intForColumn:(NSString *)columnName;
- (int)intForColumnIndex:(int)columnIdx;
/* 获取存储的长整形值 */
- (long)longForColumn:(NSString *)columnName;
- (long)longForColumnIndex:(int)columnIdx;
/* 获取存储的布尔值 */
- (BOOL)boolForColumn:(NSString *)columnName;
- (BOOL)boolForColumnIndex:(int)columnIdx;
/* 获取存储的浮点值 */
- (double)doubleForColumn:(NSString *)columnName;
- (double)doubleForColumnIndex:(int)columnIdx;
/* 获取存储的字符串 */
- (NSString *)stringForColumn:(NSString *)columnName;
- (NSString *)stringForColumnIndex:(int)columnIdx;
/* 获取存储的日期数据 */
- (NSDate *)dateForColumn:(NSString *)columnName;
- (NSDate *)dateForColumnIndex:(int)columnIdx;
/* 获取存储的二进制数据 */
- (NSData *)dataForColumn:(NSString *)columnName;
- (NSData *)dataForColumnIndex:(int)columnIdx;
/* 获取存储的UTF8格式的C语言字符串 */
- (const unsigned cahr *)UTF8StringForColumnName:(NSString *)columnName;
- (const unsigned cahr *)UTF8StringForColumnIndex:(int)columnIdx;
/* 获取存储的对象,只能是NSNumber、NSString、NSData、NSNull */
- (id)objectForColumnName:(NSString *)columnName;
- (id)objectForColumnIndex:(int)columnIdx;
6:数据库表的关闭
/*
关闭数据库,在你对数据库的操作完成以后要记得关闭数据库
*/
[citydb close];
三:一些注意事项
下面要讲一讲我在学习FMDB的时候遇到的一些问题,给我的感觉呢,FMDB在Xcode中编写其实还是挺糟心的。因为你会发现有时候稍微写错一点点东西,就能让你整个方法崩溃,有可能一个简单的(')就让你整句查询语句说拜拜= =,而且Xcode不会告诉你到底是哪里出了问题,这就让人很难过了,所以你能做到的只有孰能生巧,不断的让自己熟练,少犯这样的低级错误。
在执行查询语句的时候一定要用- (BOOL)next 方法,就算你只查一个数据,也一定要用
错误的示范(因为只查一个数据,所以自以为是的没用next方法,然后GG了(╯‵□′)╯︵┻━┻)
if ([db open]) {
NSString *sql = [NSString stringWithFormat:@"select * from city where cityId = '%@'",@"2"];
FMResultSet *rs = [db executeQuery:sql];
NSString *name = [rs stringForColumn:@"cityName"];
[db close];
}
正确的姿势应该是这样的
if ([db open]) {
NSString *sql = [NSString stringWithFormat:@"select * from city where cityId = '%@'",@"2"];
FMResultSet *rs = [db executeQuery:sql];
while ([rs next]) {
NSString *name = [rs stringForColumn:@"cityName"];
}
[db close];
}
另外就是,在操作数据库的时候,大家一定要注意多线程的问题
FMDatabase
这个类是线程不安全的,如果在多个线程同时使用一个FMDatabase
实例,会造成数据混乱问题。
为了保证线程安全,FMDB提供方便快捷的FMDatabaseQueue
类,要使用这个类,需要#import导入头文件"FMDatabaseQueue.h"
,FMDatabaseQueue
类的操作大多都和FMDatabase
很相似