標籤:
前言
FMDB是以OC的方式封裝了SQLite的C語言API,使用起來更加物件導向,省去了很多麻煩、冗餘的C語言代碼;對比蘋果內建的Core Data架構,更加輕量級和靈活;提供了多安全執行緒的資料庫操作方法,有效地防止資料混亂;FMDB同時相容ARC和非ARC工程,在編譯的時候會自動根據工程配置來調整相關的記憶體管理代碼。
使用方法
FMDB有三個主要的類
- FMDatabase 表示一個單獨的SQLite資料庫。 用來執行SQLite的命令。
- FMResultSet 表示FMDatabase執行查詢後的結果集
- FMDatabaseQueue 如果你想在多線程中執行多個查詢或更新,你應該使用該類。這是安全執行緒的。
資料庫建立
通過指定SQLite資料庫檔案路徑來建立FMDatabase對象
FMDatabase *db = [FMDatabase databaseWithPath:path];if (![db open]) { NSLog(@"資料庫開啟失敗!");}
建立FMDatabase對象時參數為SQLite資料庫檔案路徑。該路徑可以是以下三種之一:
1、具體檔案路徑
2、Null 字元串@""
- 表示會在臨時目錄建立一個空的資料庫,當FMDatabase 連結關閉時,資料庫檔案也被刪除。
3、NULL
- 會建立一個記憶體中臨時資料庫,當FMDatabase串連關閉時,資料庫會被銷毀
開啟資料庫
在和資料庫互動 之前,資料庫必須是開啟的。如果資源或許可權不足無法開啟或建立資料庫,都會導致開啟失敗,返回BOOL類型
if (![db open]) { [db release]; return; }
執行更新
- 一切不是SELECT命令的命令都視為更新。這包括 CREATE, UPDATE, INSERT,ALTER,COMMIT, BEGIN, DETACH, DELETE, DROP, END, EXPLAIN, VACUUM, and REPLACE (等)。
簡單來說,只要不是以SELECT開頭的命令都是UPDATE命令。
- 執行更新返回一個BOOL值。YES表示執行成功,否則表示有那些錯誤 。你可以調用 -lastErrorMessage 和 -lastErrorCode方法來得到更多資訊。
使用executeUpdate:方法執行更新
- (BOOL)executeUpdate:(NSString*)sql, ...- (BOOL)executeUpdateWithFormat:(NSString*)format, ...- (BOOL)executeUpdate:(NSString*)sql withArgumentsInArray:(NSArray *)arguments
樣本
[db executeUpdate:@"UPDATE t_student SET age = ? WHERE name = ?;", @20, @"Jack"]
執行查詢
SELECT命令就是查詢,執行查詢的方法是以 -excuteQuery開頭的。
查詢方法
- (FMResultSet *)executeQuery:(NSString*)sql, ...- (FMResultSet *)executeQueryWithFormat:(NSString*)format, ...- (FMResultSet *)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray *)arguments
執行查詢時,如果成功返回FMResultSet對象, 錯誤返回nil. 與執行更新相當,支援使用 NSError**參數。同時,你也可以使用 -lastErrorCode和-lastErrorMessage獲知錯誤資訊。
為了遍曆查詢結果,你可以使用while迴圈。你還需要知道怎麼跳到下一個記錄。使用FMDB,很簡單實現,就像這樣:
FMResultSet *s = [db executeQuery:@"SELECT * FROM myTable"]; while ([s next]) { //retrieve values for each record }
你必須一直調用 -[FMResultSet next] 在你訪問查詢傳回值之前,甚至你只想要一個記錄:
FMResultSet *s = [db executeQuery:@"SELECT COUNT(*) FROM myTable"]; if ([s next]) { int totalCount = [s intForColumnIndex:0]; }
FMResultSet 提供了很多方法來獲得所需的格式的值:
intForColumn:longForColumn:longLongIntForColumn:boolForColumn:doubleForColumn:stringForColumn:dataForColumn:dataNoCopyForColumn:UTF8StringForColumnIndex:objectForColumn:
這些方法也都包括 {type}ForColumnIndex 的這樣子的方法,參數是查詢結果集的列的索引位置。
你無需調用 [FMResultSet close]來關閉結果集, 當新的結果集產生,或者其資料庫關閉時,會自動關閉。
關閉資料庫
當使用完資料庫,你應該 -close 來關閉資料庫連接來釋放SQLite使用的資源。
[db close];
使用FMDatabaseQueue 及安全執行緒
在多個線程中同時使用一個FMDatabase執行個體是不明智的。現在你可以為每個線程建立一個FMDatabase對象。 不要讓多個線程分享同一個執行個體,它無法在多個線程中同時使用。 若此,壞事會經常發生,程式會時不時崩潰,或者報告異常,或者隕石會從天空中掉下來砸到你Mac Pro. 總之很崩潰。所以,不要初始化FMDatabase對象,然後在多個線程中使用。請使用 FMDatabaseQueue,它是你的朋友而且會協助你。以下是使用方法:
首先建立隊列
FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithPath:aPath];
這樣使用
[queue inDatabase:^(FMDatabase *db) { [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:1]]; [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:2]]; [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:3]]; FMResultSet *rs = [db executeQuery:@"select * from foo"]; while([rs next]) { … } }];
像這樣,輕鬆地把簡單任務封裝到事務裡
[queue inTransaction:^(FMDatabase *db, BOOL *rollback) { [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:1]]; [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:2]]; [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:3]]; if (whoopsSomethingWrongHappened) { *rollback = YES; return; } // etc… [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:4]]; }];
FMDatabaseQueue將運行在一個序列化隊列塊。所以如果你從多個線程在同一時間調用FMDatabaseQueue的方法,他們將執行他們收到的指令。這樣查詢和更新不會相互影響,每一個都是快樂的。
程式碼範例
FMDBDataBase.h
// FMDBDataBase.h#import <Foundation/Foundation.h>@interface FMDBDataBase : NSObject// 擷取資料庫管理對象單例的方法+ (FMDBDataBase *)sharedDataBase;// 關閉資料庫- (void)closeDataBase;// 清空資料庫- (BOOL)deleteDataBase;// 向搜尋記錄表中插入新紀錄- (BOOL)insertSearchText:(NSString *)searchText;// 查詢資料庫中是否包含當前搜尋記錄- (BOOL)isExistSearchText:(NSString *)searchText;// 擷取所有搜素記錄- (NSMutableArray *)getAllSearchText;// 刪除所有搜尋記錄- (BOOL)deleteAllSearchText;@end
FMDBDataBase.m
#import "FMDBDataBase.h"#import "FMDB.h"@interface FMDBDataBase ()@property (nonatomic, strong) FMDatabase *fmDataBase;@end@implementation FMDBDataBase+ (FMDBDataBase *)sharedDataBase{ static FMDBDataBase *fmdbDataBase = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ fmdbDataBase = [[FMDBDataBase alloc] init]; }); return fmdbDataBase;}- (instancetype)init{ if (self = [super init]) { _fmDataBase = [FMDatabase databaseWithPath:[self getDataBasePath]]; // 如果資料庫開啟失敗返回空值 if (![_fmDataBase open]) { return nil; } } // 如果資料庫開啟成功 建立表 // 建立搜尋歷程記錄表 NSString *searchHistorySql = @"CREATE TABLE IF NOT EXISTS t_history(t_id integer PRIMARY KEY AUTOINCREMENT, searchText text NOT NULL, age integer NOT NULL)"; BOOL isSuc = [_fmDataBase executeUpdate:searchHistorySql]; if (isSuc) { NSLog(@"建立成功!"); } return self;}- (NSString *)getDataBasePath{ // 1.獲得資料庫檔案的路徑 NSString *doc = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];// NSString *doc = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents"]; NSString *fileName = [doc stringByAppendingPathComponent:@"fmdb.sqlite"]; return fileName;}// 清空資料庫- (BOOL)deleteDataBase{ NSString *sql = @"DELETE FROM t_history"; BOOL isSuc = [_fmDataBase executeUpdate:sql]; if (isSuc) { NSLog(@"刪除成功"); return YES; } NSLog(@"刪除失敗"); return NO;}// 關閉資料庫- (void)closeDataBase{ if (_fmDataBase) { [_fmDataBase close]; }}// 查詢資料庫中是否包含當前搜尋記錄- (BOOL)isExistSearchText:(NSString *)searchText{ NSString *sql = @"SELECT * FROM t_history"; FMResultSet *results = [_fmDataBase executeQuery:sql]; while (results.next) { if ([searchText isEqualToString:[results stringForColumn:@"searchText"]]) { return YES; } } return NO;}// 搜尋記錄表中插入新記錄- (BOOL)insertSearchText:(NSString *)searchText{ if (!searchText || [searchText isEqualToString:@""] || [self isExistSearchText:searchText]) { NSLog(@"資料為空白或已存在"); return NO; } NSString *sql = @"INSERT INTO t_history(searchText) VALUES (?)"; // executeUpdate : 不確定的參數用?來佔位 BOOL isInsertSuc = [_fmDataBase executeUpdate:sql, searchText]; if (isInsertSuc) { NSLog(@"%@插入成功", searchText); return YES; } NSLog(@"%@插入失敗", searchText); return NO;}// 擷取所有的搜尋記錄- (NSMutableArray *)getAllSearchText{ NSString *sql = @"SELECT * FROM t_history order by t_id desc"; // 儲存所有資料的數組 NSMutableArray *searchTests = [NSMutableArray array]; FMResultSet *results = [_fmDataBase executeQuery:sql]; while (results.next) { NSString *result = [results stringForColumn:@"searchText"]; [searchTests addObject:result]; } return searchTests;}// 刪除所有的搜尋記錄- (BOOL)deleteAllSearchText{ NSString *sql = @"DELETE FROM t_history"; BOOL isDeleteSuc = [_fmDataBase executeUpdate:sql]; if (isDeleteSuc) { NSLog(@"刪除成功"); return YES; } return NO;}@end
注意
- 如果ID設定為逐漸,且設定為自動成長的話,那麼把表中的資料刪除後,重新插入新的資料,ID的編號不是從0開始,而是接著之前的ID進行編號。
參考文章
- FMDB官方使用文檔(翻譯)
- iOS開發資料庫篇—FMDB簡單介紹
歡迎閱讀上一篇:
- iOS資料存放區之歸檔
- iOS資料存放區之喜好設定和屬性列表
iOS-Ant-Bang互助社區 426981364
iOS技術交流群 461069757 歡迎加入!
iOS開發資料庫-FMDB