2011年6月23日 星期四

在ios使用sqlite的救贖 - FMDB

最近有個專案需要小型資料庫,當然首選一定是iOS內建的sqlite,但是因為他是C的lib,使用上是出了名的困難。之前就有聽說用sqlite可以使用FMDB這個objective-c wrapper library。果不其然的簡單易用,我馬上把我使用經驗跟大家分享,如果你想馬上體驗,也可以去抓我的Sample project - MySqlite

安裝FMDB
方法很簡單,就跟所有的open source library一樣的三步驟。
1. 首先你要先把 libsqlite3.dylib 加進library
2. 在需要用到FMDB的檔案中#import "FMDatabase.h"
3. 把FMDB的.h跟.m加進你的project中
接著就可以開心的使用FMDB

產生Database
基本上就是透過+[FMDatabase databaseWithPath]取產生Database instance。

NSURL *appUrl = [[[NSFileManager defaultManager]
URLsForDirectory:NSDocumentDirectory
inDomains:NSUserDomainMask] lastObject];
NSString *dbPath = [[appUrl path] stringByAppendingPathComponent:@"MyDatabase.db"];
FMDatabase* db = [FMDatabase databaseWithPath:dbPath];

if (![db open]) {
NSLog(@"Could not open db");
}


產生所需table
其實寫過sql的應該對接下來的動作都不陌生,都一樣就是sql語法囉,詳細的請參考sqlite文件
下面的動作是產生一個user的table,並且有uid, name, descritpion三個column。我們使用IF NOT EXISTS來保證table不存在才會產生table。

if(![db executeUpdate:@"CREATE TABLE IF NOT EXISTS user (uid integer primary key asc autoincrement, name text, description text)"])
{
NSLog(@"Could not create table: %@", [db lastErrorMessage]);
}


新增record
在FMDB中,它有提供很貼心類似-[NSString stringWithFormat:...]的功能,但是比較不一樣的是如果你是字串,它會幫你加上括號,使用上更為便利。

if(![db executeUpdate:@"INSERT INTO user (name, description) VALUES (?,?)", name, description])
{
NSLog(@"Could not insert data: %@", [db lastErrorMessage]);
}


刪除record
一樣是下sql語法,這邊比較需要注意的是如果你要傳進去的是int, double這種基本形態,記得要轉換成物件,也就是用+[NSNumber numberWithInt]去轉換

if(![_db executeUpdate:@"DELETE FROM user WHERE uid = ?", [NSNumber numberWithInt:uid]])
{
NSLog(@"Could not delete data: %@", [db lastErrorMessage]);
}


查詢資料
在FMDB中有提供另外一個class FMResultSet來存放查詢出來的records,透過-[FMResultSet next]來iterate出所有的record

NSMutableArray* items = [NSMutableArray arrayWithCapacity:0];
FMResultSet *rs = [db executeQuery:@"SELECT uid, name, description from user"];

while ([rs next]) {
int uid = [rs intForColumn:@"uid"];
NSString *name = [rs stringForColumn:@"name"];
NSString *description = [rs stringForColumn:@"description"];

[items addObject:[NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithInt:uid], @"uid",
name, @"name",
description, @"description",
nil]];
}

[rs close];


基本上在其他oo平台中有用過sql的,對於FMDB的設計應該並不陌生,可以說完全不會有太多的困難。而相較於用傳統的plist或是archive的方法,我相信不管是效能上或是功能上,Sqlite+FMDB的組合絕對會更有優勢。對於中型以上的iOS程式應該會是標準配備。

10 則留言:

  1. 感謝分享
    雖還在初學途中…
    但之後一定會用上SQLite!

    回覆刪除
  2. 感謝你的回應..
    SQLite應該多少會用到
    可以選擇直接用SQLite的c library
    或是用FMDB
    或是用Core Data..
    我目前比較喜歡用FMDB...

    回覆刪除
  3. 若要做executeUpdate的動作,前面要加個[_db beginTransaction];,然後再寫executeUpdate動作,最後再[_db commit];會比較好~

    回覆刪除
  4. 感謝補充.. 尤其是對多個修改的時候.. transaction可以保證所有資料一次性的進資料庫.. 也就是所謂的ACID原則

    回覆刪除
  5. 請問-刪除重複資料要怎麼做?
    我有參考http://www.dotblogs.com.tw/lastsecret/archive/2010/07/13/16532.aspx
    的方式,1.DELETE Product where ID NOT IN (Select Max(ID) From [Product] Group By 產品名稱)2.Select * From [Product] Where ID In (Select Max(ID) From [Product] Group By 產品名稱)
    但是沒有用,請問要怎麼做?謝謝

    回覆刪除
  6. 另外還看到http://blog.miniasp.com/post/2010/08/12/Remove-duplicate-rows-from-a-table-in-SQL-Server.aspx
    說到“若要避免資料重複,建議額外建立含有多個欄位的「唯一索引鍵」,從資料庫層級就阻擋所有可能重複資料的出現。”

    我主要是要避免資料重複,那我該怎麼建立含有多個欄位的「唯一索引鍵」?
    謝謝!!

    回覆刪除
  7. DELETE from Product where ID NOT IN (Select Max(ID) From [Product] Group By 產品名稱)
    你少了from吧?

    回覆刪除
  8. 以你的例子
    CREATE TABLE myTable (id primary key asc autoincrement, name text unique);
    這樣就可以了
    多欄位的primary key不是你這種需求用的...

    回覆刪除
  9. 你好 請問一下我使用FDMB的方法INSERT DATA
    可是每當我compiler一次他就會重複insert一樣的data

    [db executeUpdate:@"INSERT INTO User (Name) VALUES(?)",@"CHAUSTER"]

    例如上面的例子 我每次complier他就會多一筆chauster的資料
    之後就越來越多

    請問我該怎麼寫才可以避免這種情況呢
    知道要設primary key 可是不知道該怎麼寫...
    謝謝

    回覆刪除
  10. 這個問題基本上我建議先去網路上找一些sql的文章來看吧,例如
    http://www.w3schools.com/sql/default.asp
    這很難用一兩行字回答你

    回覆刪除