初識FMDB
iOS中原聲的SQLite API在進行數據存儲的時候,需要使用C語言中的函數,操作比較麻煩,於是就出現了一系列將SQLite封裝的庫。本文講解的FMDB就是其中的一個。
FMDB PK Sqlite
優點:
1.對多線程的並發操作進行了處理,所以是線程安全的
2.以OC的方式封裝了SQLite的C語言API,使用起來更加方便
3.FMDB是輕量級框架 使用靈活
缺點:
因為它是OC的語言封裝的,只能在ios開發的時候使用,所以在實現跨平台操作的時候存在局限性。
FMDB框架中重要的框架類
FMDataBase
FMDataBase對象就代表一個單獨的SQLite數據庫 用來執行SQL語句
FMResultSet
使用FNDataBase執行查詢后的結果集
FMDataBaseQueue
用於在多線程中執行多個查詢或更新 他是線程安全的
下面通過一個例子來講解FMDB的具體用法
首先使用FMDB需要導入libsqlite3.0框架,在需要數據庫的類中引入
FMDatabase.h.
創建數據庫
例子中用到的測試模型類
#import <Foundation/Foundation.h> @interface student : NSObject @property (nonatomic,assign) int stuid; @property (nonatomic,copy) NSString *name; @property (nonatomic,copy) NSString *sex; @property (nonatomic,assign) int age; @end
創建數據庫的代碼
- (void)createDataBase { //創建數據庫 //1>獲取數據庫文件的路徑 NSString *docpath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0]; NSString *fileName = [docpath stringByAppendingPathComponent:@"student.sqlite"]; //初始化數據庫 db = [[FMDatabase alloc] initWithPath:fileName]; //創建數據庫中的表 if (db) { if ([db open]) { BOOL stuSql = [db executeUpdate:@"create table if not exists stu(stuid integer primary key autoincrement, name varchar(255),sex varchar(255),age integer)"]; if (stuSql) { NSLog(@"數據庫創建表成功"); }else { NSLog(@"創建表失敗"); } }else { NSLog(@"數據庫沒有打開"); } }else { NSLog(@"創建數據庫失敗"); } }
注意創建數據庫時路徑的問題 路徑可以是以下三種方式之一
1.文件路徑 該文件路徑真實存在,如果不存在回自動創建
2.空字符串@"" 表示會在臨時目錄里創建一個空的數據庫 當FMDataBase連接關閉時 文件也會被刪除
3.NULL 將創建一個內在數據庫 同樣的 當FMDataBase連接關閉時 數據將會被銷毀
向數據庫中添加數據 查找數據 和 刪除數據
//向數據庫中插入數據 - (void)insertDataToDb{ NSArray *array = @[@"13141",@"13142",@"13143",@"13144",@"13145"]; for (int i = 0; i < 5; i ++) { NSString *insertSql = @"insert into stu(stuid,name,sex,age) values(?,?,?,?)"; BOOL success = [db executeUpdate:insertSql,array[i],[NSString stringWithFormat:@"tian%d",i],@"男",@20]; if (success) { NSLog(@"數據插入成功"); } } } //查詢數據庫 //查詢所有的數據庫數據 - (void)selectDataFormDb{ NSString *selectSql = @"select *from stu"; FMResultSet *result = [db executeQuery:selectSql]; while ([result next]) { NSLog(@"%@ %@ %@ %d",[result stringForColumn:@"stuid"],[result stringForColumn:@"name"],[result stringForColumn:@"sex"],[result intForColumn:@"age"]); } } //清空數據庫 - (void)deleteAllDbData { NSString *deleteSql = @"delete from stu"; BOOL success = [db executeUpdate:deleteSql]; if (success) { NSLog(@"刪除數據成功"); } }
清空數據庫之前的打印結果是
FMDB[1530:98012] 數據插入成功 FMDB[1530:98012] 數據插入成功 FMDB[1530:98012] 數據插入成功 FMDB[1530:98012] 數據插入成功 FMDB[1530:98012] 數據插入成功 FMDB[1530:98012] 13141 tian0 男 20 FMDB[1530:98012] 13142 tian1 男 20 FMDB[1530:98012] 13143 tian2 男 20 FMDB[1530:98012] 13144 tian3 男 20 FMDB[1530:98012] 13145 tian4 男 20
修改數據
//修改數據庫中的數據 - (void)updateDbData { //tian6是新值 代替數據庫中name = tian1的值 NSString *updateSql = @"update stu set name = ? where name = ?"; BOOL success = [db executeUpdate:updateSql,@"tian6",@"tian1"]; if (success) { [self selectDataFormDb]; } } //結果 2016-12-12 13:44:45.489 FMDB[1604:103750] 13141 tian0 男 20 2016-12-12 13:44:45.489 FMDB[1604:103750] 13142 tian6 男 20 2016-12-12 13:44:45.489 FMDB[1604:103750] 13143 tian2 男 20 2016-12-12 13:44:45.489 FMDB[1604:103750] 13144 tian3 男 20 2016-12-12 13:44:45.489 FMDB[1604:103750] 13145 tian4 男 20
下面是另寫的一個完整的例子
student.h文件
#import "ViewController.h" @interface Student : NSObject @property(nonatomic,strong)NSString*stuid; @property(nonatomic,strong)NSString*stuname; @property(nonatomic,strong)NSString*stuage; @property(nonatomic,strong)NSData * stuheadimage; @end
studentManager.h文件
#import <Foundation/Foundation.h> #import "Student.h" @interface studentDBManager : NSObject +(instancetype)shareManager; //添加一條數據到數據表中 -(BOOL)addDataWithModel:(Student*)student ConditionString:(NSString *)conditionStr andconditionValue:(NSString *)conditionValue andtable:(NSString * )table; //通過某個字段刪除一條數據; -(BOOL)deleteDataWithConditionString:(NSString *)conditionStr andconditionValue:(NSString *)conditionValue andtable:(NSString * )table; // 刪除所有的記錄 - (BOOL)deleteAllDataWithtable:(NSString *)table; //查詢一條數據; //1.查詢全部數據,2根據特定字段查詢數據; -(NSArray * )getDataWithconditionString:(NSString * )conditionstr andConditionValue:(NSString *)conditionValue allData:(BOOL)isAllData andTable:(NSString *)table; //修改某條數據 -(BOOL)updateDataWithString:(NSString*)NewStr andNewStrValue:(id)NewStrValue andConditionStr:(NSString*)conditionStr andConditionValue:(NSString*)conditionValue andTable:(NSString*)table; @end
studentManager.m文件
#import "studentDBManager.h" #import "FMDB.h" static studentDBManager * manager=nil; @implementation studentDBManager { FMDatabase * _database; } +(instancetype)shareManager { static dispatch_once_t onceTocken; dispatch_once(&onceTocken, ^{ manager=[[studentDBManager alloc]init]; }); return manager; } -(instancetype)init { if (self=[super init]) { // 創建數據庫,使用FMDB第三方框架 // 創建數據庫文件保存路徑..../Documents/app.sqlite // sqlite數據庫(輕量級的數據庫),它就是一個普通的文件,txt是一樣的,只不過其中的文件內容不一樣。 // 注:sqlite文件中定義了你的數據庫表、數據內容 // MySql、Oracle這些大型的數據庫,它需要一個管理服務,是一整套的。 NSString * dbPath=[NSString stringWithFormat:@"%@/Documents/app.sqlite",NSHomeDirectory()]; NSLog(@"%@",dbPath); // 創建FMDatabase // 如果在目錄下沒有這個數據庫文件,將創建該文件。 _database=[[FMDatabase alloc]initWithPath:dbPath]; if (_database) { if ([_database open]) { //第一步:創建學生信息表 NSString *stuSql = @"create table if not exists stu(stuid varchar(255),name varchar(255),age varchar(255),headimage binary)"; //第一步:創建學生信息表 NSString * createSql=@"create table if not exists stu(stuid varchar(255),name varchar(255),age varchar(255),headimage binary)"; // FMDatabase執行sql語句 // 當數據庫文件創建完成時,首先創建數據表,如果沒有這個表,就去創建,有了就不創建 BOOL creatableSucess=[_database executeUpdate:createSql]; NSLog(@"創建表%d",creatableSucess); } else { NSLog(@"打開數據庫失敗"); } } else { NSLog(@"創建數據庫失敗"); } } return self; } ////通過某個字段檢查是否存在數據 - (BOOL)isExsitsWithConditionString:(NSString *)conditionStr andConditionValue:(NSString *)conditionValue andtable:(NSString *)table { NSString * querySql = [NSString stringWithFormat:@"select * from %@ where %@='%@'", table,conditionStr,conditionValue]; FMResultSet * set = [_database executeQuery:querySql]; // 判斷是否已存在數據 if ([set next]) { return YES; } else return NO; } //添加一條數據到數據表中 -(BOOL)addDataWithModel:(Student*)student ConditionString:(NSString *)conditionStr andconditionValue:(NSString *)conditionValue andtable:(NSString * )table { // 如果已存在數據,先刪除已有的數據,再添加新數據 BOOL isExsits = [self isExsitsWithConditionString:conditionStr andConditionValue:conditionValue andtable:table]; if (isExsits) { [self deleteDataWithConditionString:conditionStr andconditionValue:conditionValue andtable:table]; } // 添加新數據 NSString * insertSql = [NSString stringWithFormat:@"insert into %@ values (?,?,?,?)",table]; BOOL success = [_database executeUpdate:insertSql,student.stuid ,student.stuname,student.stuage,student.stuheadimage]; NSLog(@"%d",success); return success; } //通過某個字段刪除一條數據; -(BOOL)deleteDataWithConditionString:(NSString *)conditionStr andconditionValue:(NSString *)conditionValue andtable:(NSString * )table { //刪除之前先判斷該數據是否存在; BOOL isExsits=[self isExsitsWithConditionString:conditionStr andConditionValue:conditionValue andtable:table]; if (isExsits) { NSString * deleteSql = [NSString stringWithFormat:@"delete from %@ where %@='%@'",table,conditionStr,conditionValue]; BOOL success=[_database executeUpdate:deleteSql]; return success; } else { NSLog(@"該記錄不存在"); return NO; } } // 刪除所有的記錄 - (BOOL)deleteAllDataWithtable:(NSString *)table { NSString * deletesql=[NSString stringWithFormat:@"delete from %@",table]; BOOL success = [_database executeUpdate:deletesql]; return success; } //查詢一條數據; //1.查詢全部數據,2根據特定字段查詢數據; -(NSArray * )getDataWithconditionString:(NSString * )conditionstr andConditionValue:(NSString *)conditionValue allData:(BOOL)isAllData andTable:(NSString *)table { NSString * getSql; if (isAllData) { getSql =[NSString stringWithFormat:@"select * from %@",table]; } else { getSql = [NSString stringWithFormat:@"select * from %@ where %@='%@'",table,conditionstr,conditionValue]; } // 執行sql FMResultSet * set = [_database executeQuery:getSql]; // 循環遍歷取出數據 NSMutableArray * array = [[NSMutableArray alloc] init]; while ([set next]) { Student * model = [[Student alloc] init]; // 從結果集中獲取數據 // 注:sqlite數據庫不區別分大小寫 model.stuid = [set stringForColumn:@"stuid"]; model.stuname= [set stringForColumn:@"name"]; model.stuage=[set stringForColumn:@"age"]; model.stuheadimage=[set dataForColumn:@"headimage"]; [array addObject:model]; } //備注:stuheadimage的使用, UIImage * image=[UIImage imageWithData:imageData]; return array; } //修改某條數據 -(BOOL)updateDataWithString:(NSString*)NewStr andNewStrValue:(id)NewStrValue andConditionStr:(NSString*)conditionStr andConditionValue:(NSString*)conditionValue andTable:(NSString*)table { NSString * updateSql=[NSString stringWithFormat:@"UPDATE %@ SET %@='%@' WHERE %@='%@';",table,NewStr,NewStrValue,conditionStr,conditionValue]; BOOL success= [_database executeUpdate:updateSql]; return success; }
數據庫的多線程操作
如果應用中使用了多線程操作數據庫,那么就需要使用FMDatabaseQueue來保證線程安全了。 應用中不可在多個線程中共同使用一個FMDatabase對象操作數據庫,這樣會引起數據庫數據混亂。 為了多線程操作數據庫安全,FMDB使用了FMDatabaseQueue,使用FMDatabaseQueue很簡單,首先用一個數據庫文件地址來初使化FMDatabaseQueue,然后就可以將一個閉包(block)傳入inDatabase方法中。 在閉包中操作數據庫,而不直接參與FMDatabase的管理。
FMDatabaseQueue * queue = [FMDatabaseQueue databaseQueueWithPath:database_path]; dispatch_queue_t q1 = dispatch_queue_create("queue1", NULL); dispatch_queue_t q2 = dispatch_queue_create("queue2", NULL); dispatch_async(q1, ^{ for (int i = 0; i < 50; ++i) { [queue inDatabase:^(FMDatabase *db2) { NSString *insertSql1= [NSString stringWithFormat: @"INSERT INTO '%@' ('%@', '%@', '%@') VALUES (?, ?, ?)", TABLENAME, NAME, AGE, ADDRESS]; NSString * name = [NSString stringWithFormat:@"jack %d", i]; NSString * age = [NSString stringWithFormat:@"%d", 10+i]; BOOL res = [db2 executeUpdate:insertSql1, name, age,@"濟南"]; if (!res) { NSLog(@"error to inster data: %@", name); } else { NSLog(@"succ to inster data: %@", name); } }]; } }); dispatch_async(q2, ^{ for (int i = 0; i < 50; ++i) { [queue inDatabase:^(FMDatabase *db2) { NSString *insertSql2= [NSString stringWithFormat: @"INSERT INTO '%@' ('%@', '%@', '%@') VALUES (?, ?, ?)", TABLENAME, NAME, AGE, ADDRESS]; NSString * name = [NSString stringWithFormat:@"lilei %d", i]; NSString * age = [NSString stringWithFormat:@"%d", 10+i]; BOOL res = [db2 executeUpdate:insertSql2, name, age,@"北京"]; if (!res) { NSLog(@"error to inster data: %@", name); } else { NSLog(@"succ to inster data: %@", name); } }]; } });
上面就是iOS中FMDB數據庫框架的一些基本應用.