将 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中重要的類
- FMDatabase:一個 FMDatabase 對象就代表一個單獨的 SQLite資料庫,用來執行 SQL語句。
- FMResultSet:使用 FMDatabase 執行查詢後的結果集。
- FMDatabaseQueue:用于在多線程中執行多個查詢或更新,它是線程安全的。
4> FMDB使用步驟
- 第一步:使用 CocoaPods 将第三方內建到工程項目中
- 第二步:導入 libsqlite3.0 架構,導入頭檔案 FMDatabase.h
- 第三步:代碼實作,與 SQLite 使用步驟相似,建立資料庫路徑,獲得資料庫路徑,打開資料庫,然後對資料庫進行增、删、改、查操作,最後關閉資料庫。
2. FMDB建立資料庫和資料表
- 第一步:獲得資料庫檔案的路徑
建立FMDatabase對象時參數為SQLite資料庫檔案路徑,該路徑可以是以下三種方式之一:
① 檔案路徑。該檔案路徑無需真實存在,如果不存在會自動建立;
② 空字元串(@"")。表示會在臨時目錄建立一個空的資料庫,當 FMDatabase 連接配接關閉時,檔案也會被删除;
③ NULL。将建立一個内在資料庫,同樣的,當 FMDatabase 連接配接關閉時,資料将會被銷毀。
我們一般采用第一種方式來獲得資料庫檔案的路徑,具體執行個體代碼如下:
NSString *documentPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
self.filePath = [documentPath stringByAppendingPathComponent:@"student.sqlite"];
NSLog(@"filePath = %@", self.filePath);
- 第二步:使用路徑初始化FMDB對象
使用的初始化方法:
+ (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實作多線程操作
- 如果應用中使用了多線程操作資料庫,那麼就需要使用 FMDatabaseQueue 來保證線程安全了。 應用中不可在多個線程中共同使用一個 FMDatabase 對象操作資料庫,這樣會引起資料庫資料混亂(例如使用兩個線程同時對資料庫進行更新和查找)。
- 多個線程更新相同的資源導緻資料競争時使用等待隊列(等待現在執行的處理結束)。
- 以隊列的形式添加是 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];
}