FMDB的简单使用
- 导入头文件 #import "FMDB.h"
- 全局的静态数据库对象
initialize
// 这个类在第一次使用时就被调用一次,后面就不会再调用;而且是先于实例化方法调用的.
static FMDatabase *_db;
+ (void)initialize {
// NSLog(@"这个类在第一次使用时就被调用一次");
// 1.获取数据库路径
NSString *SQLPath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"heros.db"];//heros是你的表名
// 2.创建数据库和建表,只会执行一次
_db = [FMDatabase databaseWithPath:SQLPath];
// 3.打开数据库
BOOL isOpen = [_db open];
if (isOpen) {
// 4.建表
BOOL isCreate = [_db executeUpdate:@"create table if not exists t_heros(id integer primary key,name text not null,age integer)"];
if (isCreate) {
NSLog(@"建表成功");
}
}
}
新增
[_db executeUpdateWithFormat:@"insert into t_heros(name,age) values('张三',18)"];
修改
[_db executeUpdateWithFormat:@"update t_heros set age = 19 where name = '张三'"];
删除
[_db executeUpdateWithFormat:@"delete from t_heros where name = '张三'"];
查询 -- 模糊查询
// 执行查询语句.获取到结果集
FMResultSet *resultSet = [_db executeQuery:@"select * from t_heros"];
// 遍历结果集,取数据
while ([resultSet next]) {
// 取出来的数据
NSString *name = [resultSet stringForColumn:@"name"];
int age = [resultSet intForColumn:@"age"];
}
如果数据库里面的记录有很多条,那么就要像解析模型一样创建数据源去接收多条数据存到数据源
+ (NSArray *)selectHeros{
// 定义临时的模型数据
NSMutableArray *tmpM = [NSMutableArray array];
// 执行查询语句.获取到结果集
FMResultSet *resultSet = [_db executeQuery:@"select * from t_heros"];
// 遍历结果集,取数据
while ([resultSet next]) {
// 创建模型
Heros *hero = [[Heros alloc] init]; //自己的模型
// 取出来的数据
NSString *name = [resultSet stringForColumn:@"name"];
int age = [resultSet intForColumn:@"age"];
// 给模型赋值 ,name和age为模型里面的key,根据自己的实际情况更改
hero.name = name;
hero.age = @(age);
// 将模型添加到模型数组
[tmpM addObject:hero];
}
return tmpM.copy;
到此简单的数据库增删改查都可以实现,那么问题来了
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[_db executeUpdateWithFormat:@"insert into t_heros(name,age) values('李四',19)"];
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[_db executeUpdateWithFormat:@"insert into t_heros(name,age) values('周五',20)"];
});
项目中不可能只是单线程的情况,那么多线程情况下就有可能出现多条数据插入,只有一条数据成功的情况,因为FMDB默认不支持多线程去操作数据库,
分析FMDatabaseQueue
源码
- (void)inDatabase:(void (^)(FMDatabase *db))block {
/* Get the currently executing queue (which should probably be nil, but in theory could be another DB queue * and then check it against self to make sure we're not about to deadlock. */
FMDatabaseQueue *currentSyncQueue = (__bridge id)dispatch_get_specific(kDispatchQueueSpecificKey);
assert(currentSyncQueue != self && "inDatabase: was called reentrantly on the same queue, which would lead to a deadlock");
FMDBRetain(self);
// 串行队列+同步任务
dispatch_sync(_queue, ^() {
// 数据库实例
FMDatabase *db = [self database];
block(db);
if ([db hasOpenResultSets]) {
NSLog(@"Warning: there is at least one open result set around after performing [FMDatabaseQueue inDatabase:]");
#if defined(DEBUG) && DEBUG
NSSet *openSetCopy = FMDBReturnAutoreleased(
[[db valueForKey:@"_openResultSets"] copy]);
for (NSValue *rsInWrappedInATastyValueMeal in openSetCopy) {
FMResultSet *rs = (FMResultSet *)[rsInWrappedInATastyValueMeal pointerValue];
NSLog(@"query: '%@'", [rs query]);
}
#endif
}
});
FMDBRelease(self);}
结论 :
FMDatabaseQueue管理着一个队里,这个队列是串行队列.
串行队列里面装的都是同步任务
同步任务里面就是操作数据库的代码
所以如果我们的 app 需要多线程操作数据库,那么就需要使用 FMDatabaseQueue 来保证线程安全了。切记不能在多个线程中共同一个 FMDatabase 对象并且在多个线程中同时使用,这个类本身不是线程安全的,这样使用会造成数据混乱等问题,我么需要实例化一个 FMDatabaseQueue区吃力多线程下的数据库操作
封装FMDatabaseQueue单例类
把FMDatabaseQueue定义成单例的目的是为了保证队列在内存中是唯一的一个,当有多个数据库操作任务时,都可以放在同一个队列中.
单例类继承自FMDatabaseQueue
#import "FMDB.h"
//继承FMDatabaseQueue
@interface FMDatabaseQueueManager : FMDatabaseQueue
+ (instancetype)sharedDatabaseQueue;
@end
FMDatabaseQueue
单例类实现
+ (instancetype)sharedDatabaseQueueManager{
static FMDatabaseQueueManager *instance;
static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{
// 初始化队列的操作
instance = [FMDatabaseQueueManager databaseQueueWithPath:@"/xxx/xxx/xxx/heros.db"];//数据库路径
});
return instance;
}
//实例化单利之后再进行数据库的操作
//创建数据库表
+ (void)initialize{
// 创建和打开数据库
[[FMDatabaseQueueManager sharedDatabaseQueue] inDatabase:^(FMDatabase *db) {
// 建表
BOOL isCreate = [db executeUpdate:@"create table if not exists t_heros(id integer primary key,name text not null,age integer)"];
if (isCreate) {
NSLog(@"建表成功");
}
}];
}
//以新增为例
[[FMDatabaseQueueManager sharedDatabaseQueue] inDatabase:^(FMDatabase *db) {
[db executeUpdateWithFormat:@"insert into t_heros(name,age) values('张三',28)"];