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)"];