最近在研究iOS實用的ORM (Object-Relational Mapping),翻成中文就是物件關係對映,還不是很懂?直接說是資料庫 (Database),這樣豁然開朗了嗎?儘管實際上定義有所差異,不過為了好上手,總是會想類比我們熟悉的目標。

要研究總有個方向,在Github上找到最多人給星的FMDB,發展至今已經有9852顆星,想必它之所以受到青睞,是因為它把資料庫的事情簡單化了!
FMDB是架構在SQLite發展出來的套件,由於SQLite在iOS上的使用不是平易近人,於是就有人想要改造它,促使今日FMDB的誕生呢!
FMDB是什麼?
FMDB是一款簡潔、易用的封裝庫。它是對libsqlite3框架的封裝,使用起來的過程與SQLite的使用相似,而且它對於多線程有處理並發操作,所以它是線程安全的。
This is an Objective-C wrapper around SQLite: http://sqlite.org/
FMDB v.s. Sqlite
- 優點
- 對多線程的並發操作進行處理,所以是線程安全的;
- 以OC的方式封裝了SQLite的C語言API,使用起來更加的方便;
- FMDB是輕量級的框架,使用靈活度高。
- 缺點
- 它是OC的語言封裝的,只能在iOS開發的時候使用,所以在實現跨平台有局限性。
FMDB class
- FMDatabase
- FMDatabase對象就代表一個單獨的SQLite資料庫,用來執行SQL語句
- FMResultSet
- 使用FMDatabase執行查詢後的結果集
- FMDatabaseQueue
- 用於在多線程中執行多個查詢或更新,它是線程安全的
如何使用FMDB?
只要從Github下載回來匯入專案,或是經由Cocoapods下載到專案,引用Library: libsqlite3.0.tbd和Header: FMDatabase.h,就能開始寫碼。
它到底多好用?我們當然要實際來測試!整個實作如下:
/**
Theme: FMDB Test
IDE: Xcode 7
Language: Objective C
Date: 105/06/03
Author: HappyMan
Blog: https://cg2010studio.wordpress.com/
*/
- (void)viewDidLoad {
[super viewDidLoad];
// 建立資料庫和表格
[self createDatabaseAndTable];
// 插入資料
[self insertData];
// 更新資料
[self updateData];
// 丟棄表格
// [self dropTable];
// 選擇資料
[self selectData];
// 使用佇列
[self useQueue];
}
-(void)createDatabaseAndTable
{
// 1.獲得數據庫文件的路徑
NSString *doc = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSString *fileName = [doc stringByAppendingPathComponent:@"student.sqlite"];
// 2.獲得數據庫
db = [FMDatabase databaseWithPath:fileName];
// 3.使用如下語句,如果打開失敗,可能是權限不足或者資源不足。通常打開完操作操作後,需要調用close方法來關閉數據庫。在和數據庫交互之前,數據庫必須是打開的。如果資源或權限不足無法打開或創建數據庫,都會導致打開失敗。
if ([db open])
{
// 4.創建表單
BOOL result = [db executeUpdate:@"CREATE TABLE IF NOT EXISTS t_student (number integer PRIMARY KEY AUTOINCREMENT, name text NOT NULL, age integer NOT NULL);"];
if (result)
{
NSLog(@"創建表單成功");
}
}
}
-(void)insertData
{
NSString *name = @"HappyBoy";
int age = 16;
// 1.executeUpdate:不確定的參數用?來佔位(後面參數必須是oc對象,;代表語句結束)
[db executeUpdate:@"INSERT INTO t_student (name, age) VALUES (?,?);", name, @(age)];
// 2.executeUpdateWithForamat:不確定的參數用%@,%d等來佔位(參數為原始數據類型,執行語句不區分大小寫)
// [db executeUpdateWithFormat:@"insert into t_student (name, age) values (%@,%i);", name, age];
// 3.參數是數組的使用方式
[db executeUpdate:@"INSERT INTO t_student (name, age) VALUES (?,?);" withArgumentsInArray:@[name, @(age)]];
}
-(void)updateData
{
// 1.不確定的參數用?來佔位(後面參數必須是OC對象,需要將int包裝成OC對象)
int idNum = 3;
[db executeUpdate:@"delete from t_student where number = ?;", @(idNum)];
// 2.不確定的參數用%@,%d等來佔位
[db executeUpdateWithFormat:@"delete from t_student where name = %@;", @"Happyboy"];
// 修改學生的名字
[db executeUpdate:@"update t_student set name = ? where name = ?", @"HappyGL", @"HappyBoy"];
}
-(void)dropTable
{
// 如果表格存在則銷毀
[db executeUpdate:@"drop table if exists t_student;"];
}
-(void)selectData
{
// 查詢整個表
FMResultSet *resultSet = [db executeQuery:@"select * from t_student;"];
// 根據條件查詢
// FMResultSet *resultSet = [db executeQuery:@"select * from t_student where number <?;", @(20)];
NSString *resultStr = @"";
// 遍歷結果集合
while ([resultSet next]) {
int idNum = [resultSet intForColumn:@"number"];
NSString *name = [resultSet stringForColumn:@"name"];
int age = [resultSet intForColumn:@"age"];
NSString *rowStr = [NSString stringWithFormat:@"number: %i, name: %@, age: %i\n", idNum, name, age];
resultStr = [resultStr stringByAppendingString:rowStr];
resultTV.text = resultStr;
NSLog(@"%@", rowStr);
}
}
-(void)useQueue
{
NSString *doc = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSString *fileName = [doc stringByAppendingPathComponent:@"student.sqlite"];
NSString *name = @"HappyJoy";
int age = 36;
// 1.創建佇列
FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithPath:fileName];
__block BOOL whoopsSomethingWrongHappened = YES;
// 2.把任務包裝到合同裡
[queue inTransaction:^(FMDatabase *dbx, BOOL *rollback)
{
whoopsSomethingWrongHappened &= [dbx executeUpdate:@"INSERT INTO t_student (name, age) VALUES (?,?);", name, @(age)];
whoopsSomethingWrongHappened &= [dbx executeUpdate:@"INSERT INTO t_student (name, age) VALUES (?,?);", name, @(age+1)];
whoopsSomethingWrongHappened &= [dbx executeUpdate:@"INSERT INTO t_student (name, age) VALUES (?,?);", name, @(age+2)];
// 如果有錯誤返回
if (!whoopsSomethingWrongHappened)
{
*rollback = YES ;
return ;
}
}];
}

在此我要將student.sqlite抽出來,可以參考我先前的文章:透過iTunes檔案共享。抽出來之後我再使用SQLite Database Browser來查看!

專案中我陸續做了動作:
- 建立資料庫和表格
- 插入資料
- 更新資料
- 丟棄表格
- 選擇資料
- 使用佇列
注意SQL語法正確,確實有將資料塞進SQLite中!
在App介面我顯示從SQLite撈到的所有資料。

之後可以研究更高深的用法!
測試專案放在Github Repository: FMDBTest。如果覺得不錯的話,幫我加顆星星喔:)~
參考:
Comments on: "[iOS] FMDB 資料庫" (2)
這次接手的專案,裡頭有用到 FMDB,直接以關鍵字搜尋,第一頁就有我的文章,才想起我有研究過!😆
讚讚
[…] 上次提到FMDB 資料庫,這次講解DBAccess 資料庫,我一個月前才研究,2016/6/23要講給同事們聽時,便發現DBAccess被Shark吃掉了⋯⋯ […]
讚讚