iOS學習47之第三方-FMDB,ios47-fmdb
將 CocoaPods 安裝後,按照 CocoaPods 的使用說明就可以將 FMDB 第三方整合到工程中,具體請看部落格iOS學習46之第三方CocoaPods的安裝和使用(通用方法)
1. FMDB簡介 1> 概述
iOS 中原生的 SQLite API 在進行資料存放區的時候,需要使用 C語言 中的函數,操作比較繁瑣。於是,就出現了一系列將SQLite API 進行封裝的庫,例如 FMDB、PlausibleDatabase、SQLitePersistentObjects 等。
FMDB 是一款簡潔、易用的封裝庫。因此,在這裡推薦使用第三方架構 FMDB,它是對 libsqlite3 架構的封裝,用起來的步驟與 SQLite 使用類似,並且它對於多線程的並行作業進行了處理,所以是安全執行緒的。
2> FMDB優缺點
對多線程的並行作業進行處理,所以是安全執行緒的;
以OC的方式封裝了SQLite的C語言API,使用起來更加的方便;
FMDB是輕量級的架構,使用靈活。
因為它是OC的語言封裝的,只能在iOS開發的時候使用,所以在實現跨平台操作的時候存在局限性。
3> FMDB中重要的類
- FMDatabaseQueue:用於在多線程中執行多個查詢或更新,它是安全執行緒的。
4> FMDB使用步驟
第一步:使用 CocoaPods 將第三方整合到工程項目中
第二步:匯入 libsqlite3.0 架構,匯入標頭檔 FMDatabase.h
第三步:代碼實現,與 SQLite 使用步驟相似,建立資料庫路徑,獲得資料庫路徑,開啟資料庫,然後對資料庫進行增、刪、改、查操作,最後關閉資料庫。
2. FMDB建立資料庫和資料表
建立FMDatabase對象時參數為SQLite資料庫檔案路徑,該路徑可以是以下三種方式之一:
① 檔案路徑。該檔案路徑無需真實存在,如果不存在會自動建立;
② Null 字元串(@"")。表示會在臨時目錄建立一個空的資料庫,當 FMDatabase 串連關閉時,檔案也會被刪除;
③ NULL。將建立一個內在資料庫,同樣的,當 FMDatabase 串連關閉時,資料將會被銷毀。
我們一般採用第一種方式來獲得資料庫檔案的路徑,具體執行個體代碼如下:
NSString *documentPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0]; self.filePath = [documentPath stringByAppendingPathComponent:@"student.sqlite"]; NSLog(@"filePath = %@", self.filePath);
使用的初始化方法:
+ (instancetype)databaseWithPath:(NSString*)aPath
執行個體代碼:
// 第四步:使用路徑初始化FMDB對象 self.database = [FMDatabase databaseWithPath:self.filePath];
如果許可權不足或者資源不足,則無法開啟和建立資料庫。
// 需要判斷資料庫開啟的時候才進行執行語句 if (self.database.open) { // 建立表 }
使用的執行SQL語句的方法:
- (BOOL)executeUpdate:(NSString*)sql, ...
該方法的傳回值是一個BOOL值,我們可以根據傳回值,來判斷SQL語句是否執行成功。
執行個體代碼:
if (self.database.open) { // 建表語句 NSString *createSql = @"create table if not exists t_student(id integer primary key autoincrement not null, name text not null, age integer not null, sax text not null)"; // 執行建表語句,建立資料表 BOOL result = [self.database executeUpdate:createSql]; // 判斷是否建表成功 if (result) { NSLog(@"創表成功"); } else { NSLog(@"創表失敗"); } }
執行個體代碼:
// 第五步:關閉資料庫 [self.database close];
3. FMDB實現增、刪、改、查 1> FMDB—執行更新
一切不是SELECT命令的命令都視為更新。這包括CREAT,UPDATE,INSERT,ALTER,BEGIN,COMMIT,DETACH,DELETE,DROP,END,EXPLAIN,VACUUM,REPLACE等。
簡單來說,只要不是以 SELECT 開頭的命令都是更新命令。
執行更新返回一個BOOL值。YES表示執行成功,否則表示有錯誤。你可以調用 -lastErrorMessage 和 -lastErrorCode方法來得到更多資訊。
2> 執行更新命令的相關方法
- executeUpdate: 不確定的參數用 ? 來佔位(後面參數必須是 OC 對象,;代表語句結束,也可以不寫)
增、刪、改的代碼執行個體:
// 增加(插入)資料BOOL result = [self.database executeUpdate:@"insert into t_student(name, age, sax) values(?, ?, ?)", @"xiaoming", @12, @"男"];// 更新資料BOOL result = [self.database executeUpdate:@"update t_student set name = ? where name = ?", @"xiaoming", @"小明"];// 刪除資料BOOL result = [self.database executeUpdate:@"delete from t_student where name = ?", @"xiaoming"];
- executeUpdateWithFormat:不確定的參數用%@,%d等來佔位 (參數為未經處理資料類型,執行語句不區分大小寫)
增、刪、改的代碼執行個體:
// 增加(插入)資料BOOL result = [self.database executeUpdateWithFormat:@"insert into t_student (name, age, sax) values (%@, %i, %@);", @"xiaoming", @69, @"男"];// 更新資料BOOL result = [self.database executeUpdateWithFormat:@"update t_student set name = %@ where name = %@", @"xiaoming", @"小明"];// 刪除資料BOOL result = [self.database executeUpdateWithFormat:@"delete from t_student where name = %@", @"xiaoming"];
- executeUpdate:withArgumentsInArray:數組,直接使用數組
增、刪、改的代碼執行個體:
// 增加(插入)資料[self.database executeUpdate:@"insert into t_student(name, age, sax) values(?, ?, ?);" withArgumentsInArray:@[@"xiaoming", @12, @"男"]];// 更新資料[self.database executeUpdate:@"update t_student set name = ? where name = ?;" withArgumentsInArray:@[@"xiaoming", @"小明"]];// 刪除資料[self.database executeUpdate:@"delete from t_student where name = ?;" withArgumentsInArray:@[@"xiaoming"]];
以上的方法大家可以根據自己的習慣和需求選擇一種即可。
3> FMDB—查詢資料 ① 概述
SELECT 命令就是查詢,執行查詢的方法是以 -excuteQuery 開頭的。
執行查詢時,如果成功返回 FMResultSet 對象,錯誤返回 nil 。與執行更新相同,支援使用 NSError 參數。
同時,你也可以使用 -lastErrorCode 和 -lastErrorMessage 獲知錯誤資訊。
② FMResultSet
FMResultSet 提供了很多方法,來擷取對應欄位的資訊:
intForColumn:、longForColumn:、longLongIntForColumn:、boolForColumn:、doubleForColumn:、stringForColumn:、dataForColumn:、dataNoCopyForColumn:、UTF8StringForColumnIndex:、objectForColumn:
③ 執行查詢語句
查詢整個表
// 查詢結果使用的類FMResultSet FMResultSet *resultSet = [self.database executeQuery:@"select * from t_student"];
根據條件查詢
//根據條件查詢 FMResultSet *resultSet = [self.db executeQuery:@"select * from t_student where id<?;", @14];
④ 遍曆結果集合
while (resultSet.next) { NSInteger ID = [resultSet intForColumn:@"id"]; NSString *name = [resultSet objectForColumnName:@"name"]; NSInteger age = [resultSet intForColumn:@"age"]; NSString *sax = [resultSet objectForColumnName:@"sax"]; NSLog(@"id = %ld name = %@, age = %ld, sax = %@", ID, name, age, sax); }
4> 完整執行個體代碼
#import "ViewController.h"// 第一步:引入架構,引入支援的類庫(libsqlite3.0.tbd)#import <FMDB.h>@interface ViewController ()/// 聲明資料庫物件@property (nonatomic, strong) FMDatabase *database;/// 聲明儲存路徑@property (nonatomic, strong) NSString *filePath;@end@implementation ViewController- (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. [self createTabe];}#pragma mark - 建立表- (void)createTabe{ // 第一步:建立sql語句 NSString *createSql = @"create table if not exists t_student(id integer primary key autoincrement not null, name text not null, age integer not null, sax text not null)"; // 第二步:找到儲存路徑 NSString *documentPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];// NSLog(@"document = %@", documentPath); self.filePath = [documentPath stringByAppendingPathComponent:@"student.sqlite"]; NSLog(@"filePath = %@", self.filePath); // 第三步:使用路徑初始化FMDB對象 self.database = [FMDatabase databaseWithPath:self.filePath]; // 第四步:資料庫執行相關的操作 // 需要判斷資料庫開啟的時候才進行執行語句 if (self.database.open) { BOOL result = [self.database executeUpdate:createSql]; if (result) { NSLog(@"創表成功"); } else { NSLog(@"創表失敗"); } } // 第五步:關閉資料庫 [self.database close];}#pragma mark - 插入- (IBAction)insertIntoAction:(id)sender{ // 第一步:開啟資料庫 [self.database open]; // 第二步:進行相關的操作 NSArray *nameArray = @[@"MBBoy", @"BoomSky", @"小明"]; for (NSString *name in nameArray) { BOOL result = [self.database executeUpdate:@"insert into t_student(name, age, sax) values(?, ?, ?)", name, @69, @"男"]; // integer的資料不能在這裡使用,必須使用一個對象型資料,比如NSNumber、NSString... [self.database executeUpdate:@"INSERT INTO t_student(name, age, sax) VALUES (?, ?, ?);" withArgumentsInArray:@[@"xiaoming", @12, @"男"]]; if (result) { NSLog(@"插入成功"); } else { NSLog(@"插入失敗"); } } [self.database close];// 更新資料// 刪除資料// 增加(插入)資料}#pragma mark - 更新- (IBAction)updateAction:(id)sender{ [self.database open]; BOOL result = [self.database executeUpdate:@"update t_student set name = ? where name = ?", @"xiaoming", @"小明"]; if (result) { NSLog(@"更新成功"); } else { NSLog(@"更新失敗"); } [self.database close];}#pragma mark - 刪除- (IBAction)deleteAction:(id)sender{ [self.database open]; BOOL result = [self.database executeUpdate:@"delete from t_student where name = ?", @"MBBoy"]; if (result) { NSLog(@"刪除成功"); } else { NSLog(@"刪除失敗"); } [self.database close];}#pragma mark - 查詢- (IBAction)selectAction:(id)sender{ [self.database open]; // 查詢結果使用的類FMResultSet FMResultSet *resultSet = [self.database executeQuery:@"select * from t_student"]; // 遍曆出需要的結果內容 while (resultSet.next) { NSInteger ID = [resultSet intForColumn:@"id"]; NSString *name = [resultSet objectForColumnName:@"name"]; NSInteger age = [resultSet intForColumn:@"age"]; NSString *sax = [resultSet objectForColumnName:@"sax"]; NSLog(@"id = %ld name = %@, age = %ld, sax = %@", ID, name, age, sax); } [self.database close];}@end
4. FMDB實現多線程操作 1> 概述
多個線程更新相同的資源導致資料競爭時使用等待隊列(等待現在執行的處理結束)。
以隊列的形式添加是 FMDB 比較常用的添加方式。
FMDB 不支援多個線程同時操作,一般使用串列方式實現相關的操作。
2> 建立操作隊列
使用的初始化方法
+ (instancetype)databaseQueueWithPath:(NSString*)aPath
3> 把操作打包放在操作隊列中
打包的方法
- (void)inTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block
在 Block 中添加串列隊列
4> 執行個體代碼
#pragma mark - 以隊列的形式添加很多學生- (IBAction)insertManyStudent:(id)sender{ // 以隊列的形式新增學生是FMDB比較常用的添加方式 // FMDB不支援多個線程同時操作,一般使用串列方式實現相關的操作 [self.database open]; // 第一步:建立操作隊列 FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithPath:self.filePath]; // 標識:記錄是否操作成功 __block BOOL isSucceed = YES; // 第二步:把操作打包放在操作隊列中 NSString *insertSql = @"insert into t_studen(name, age, sax) values(?, ?, ?)"; [queue inTransaction:^(FMDatabase *db, BOOL *rollback) { // 串列隊列 isSucceed &= [db executeUpdate:insertSql, @"隔壁老王", @38, @"男"]; isSucceed &= [db executeUpdate:insertSql, @"Black", @18, @"女"]; isSucceed &= [db executeUpdate:insertSql, @"-1", @23, @"男"]; if (!isSucceed) { // block 返回的參數rollback進行處理(BOOL類型的指標) *rollback = YES; return; } else { NSLog(@"以隊列的形式添加成功"); } }]; [self.database close];}