最近在研究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吃掉了⋯⋯ […]
讚讚