Just a Computer Graphics Studio & My Life

[iOS] FMDB 資料庫

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

sqlite.png

要研究總有個方向,在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.tbdHeader: 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 = @"HappyGirl";
    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", @"Happygirl"];
}

-(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 ;
         }
     }];
}

itunes.png

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

SQLite Database Browser

專案中我陸續做了動作:

  1. 建立資料庫和表格
  2. 插入資料
  3. 更新資料
  4. 丟棄表格
  5. 選擇資料
  6. 使用佇列

注意SQL語法正確,確實有將資料塞進SQLite中!

在App介面我顯示從SQLite撈到的所有資料。

app screenshot

之後可以研究更高深的用法!

測試專案放在Github Repository: FMDBTest。如果覺得不錯的話,幫我加顆星星喔:)~

參考:

Advertisements

發表留言

在下方填入你的資料或按右方圖示以社群網站登入:

WordPress.com Logo

您的留言將使用 WordPress.com 帳號。 登出 / 變更 )

Twitter picture

您的留言將使用 Twitter 帳號。 登出 / 變更 )

Facebook照片

您的留言將使用 Facebook 帳號。 登出 / 變更 )

Google+ photo

您的留言將使用 Google+ 帳號。 登出 / 變更 )

連結到 %s

標籤雲

%d 位部落客按了讚: