深入淺出:SQLite(FMDB)

一直在構思這篇文章要寫一些什么內容睬愤,抱著負責任的態(tài)度,去看了FMDB的文檔劲蜻,發(fā)現文檔不是很長陆淀,并且基本把該寫的東西全部都寫了,于是我決定這篇文章就是把FMDB的文檔翻譯一遍先嬉。
噴子們看清楚了轧苫,我這里寫了是把文檔翻譯了一遍。
(有的地方真的是只可意會啊疫蔓,翻譯成中文就變得怪怪的)

installed#

你可以使用CocoaPods來安裝FMDB

pod 'FMDB'
# pod 'FMDB/FTS'   # FMDB with FTS
# pod 'FMDB/standalone'   # FMDB with latest SQLite amalgamation source
# pod 'FMDB/standalone/FTS'   # FMDB with latest SQLite amalgamation source and FTS
# pod 'FMDB/SQLCipher'   # FMDB with SQLCipher

當然你也可以直接拖入源文件含懊,到https://github.com/ccgus/fmdb 下載源文件,然后直接將scr->fmdb文件夾拖入到你的工程就OK衅胀。
當然你需要添加依賴庫:libsqlite3.dylib
(xcode7以后你需要添加的是libsqlite3.tbd)

拖入源文件岔乔,并且添加依賴庫以后你就可以使用FMDB了,當然你需要引用頭文件
<code>#import "FMDB.h"</code>

ARC還是MRC#

隨你便啦滚躯,FMDB很聰明的重罪,他可以根據你的工程來執(zhí)行正確的操作,所以你不用擔心他會做一些不正確的事情而造成一些不好的后果哀九。
*(不過我只在ARC下使用過剿配,并沒有真正的在MRC下驗證過,不過文檔中說是沒有問題)

使用#

在FMDB中主要有三個類:
1阅束、<code>FMDatabase</code> - 簡單的說這個類就是代表了數據庫
2呼胚、<code>FMResultSet</code> - 這個類表示查詢操作的結果
3、<code>FMDatabaseQueue</code> - 多線程操作的時候你會用到這個類息裸,并且這是線程安全蝇更,具體怎么用后面再說。

數據庫的創(chuàng)建#

你需要使用一個path來創(chuàng)建一個本地FMDatabase數據庫呼盆,這個path有三種類型:

1年扩、你可以使用一個本地的地址來創(chuàng)建這個數據庫,這個地址不一定真實存在访圃,如果不存在厨幻,那么FMDB會創(chuàng)建這個數據庫并返回,存在則直接返回這個數據庫腿时。
2况脆、一個空字符串@"".FMDB會在本地創(chuàng)建一個臨時的數據庫,當數據庫關閉的時候會刪除這個數據庫批糟。
3格了、NULL.如果你將這個path填的是NULL,那么這個數據被創(chuàng)建在內存中徽鼎,數據庫關閉的時候被銷毀盛末。
(更多信息關于臨時數據庫和在內存中的數據庫弹惦,你可以閱讀這篇文檔 http://www.sqlite.org/inmemorydb.html )

FMDatabase *db = [FMDatabase databaseWithPath:@"/tmp/tmp.db"];

Opening#

使用數據庫之前你必須打開數據庫。

if (![db open]) {
    [db release];
    return;
}

Executing Updates

所有不是select操作的操作都算update. 包括 CREATE, UPDATE, INSERT, ALTER, COMMIT, BEGIN, DETACH, DELETE, DROP, END, EXPLAIN, VACUUM, and REPLACE ····換句話說也就是如果你的操作不是以SELECT開頭的都是update操作.

update操作返回一個布爾值悄但,YES表示操作成功肤频,NO表示你可以遇到了一些錯誤.
<code>FMDatabase</code>有兩個方法 -lastErrorMessage 和 -lastErrorCode,你可以使用這兩個方法來查看錯誤算墨。

Executing Queries#

SELECT 查詢使用 -executeQuery... 方法.

查詢成功返回<code> FMResultSet</code>,失敗則是返回nil.
同樣你可以使用<code>FMDatabase</code>的兩個方法 -lastErrorMessage and -lastErrorCode 來查找原因宵荒。

你需要用一個循環(huán)來獲取到查詢到的每一個值。like this:

FMResultSet *s = [db executeQuery:@"SELECT * FROM myTable"];
while ([s next]) {
    //retrieve values for each record
}

不管如何你都必須使用方法 -[FMResultSet next]來獲取到每一個查詢結果净嘀。

FMResultSet *s = [db executeQuery:@"SELECT COUNT(*) FROM myTable"];
if ([s next]) {
    int totalCount = [s intForColumnIndex:0];
}

FMResultSet 有許多類型用來返回不同類型的查詢結果的值

intForColumn:
longForColumn:
longLongIntForColumn:
boolForColumn:
doubleForColumn:
stringForColumn:
dateForColumn:
dataForColumn:
dataNoCopyForColumn:
UTF8StringForColumnName:
objectForColumnName:

以上的每個方法都有對應的 {type}ForColumnIndex: 上面那一溜方法是用例的名字來獲取數據报咳,而這個方法則是用數據在查詢結果中對應的位置來獲取數據

當你使用完了<code>FMResultSet</code>以后你不需要close FMResultSet因為當<code>FMDatabase</code>關閉以后<code>FMResultSet</code>也同樣會關閉。

Closing#

用完了FMDB記得關挖藏。

[db close];

批處理#

<code>FMDatabase</code>的方法 executeStatements:withResultBlock:可以使用字符串來同時處理多條指令暑刃。

NSString *sql = @"create table bulktest1 (id integer primary key autoincrement, x text);"
                 "create table bulktest2 (id integer primary key autoincrement, y text);"
                 "create table bulktest3 (id integer primary key autoincrement, z text);"
                 "insert into bulktest1 (x) values ('XXX');"
                 "insert into bulktest2 (y) values ('YYY');"
                 "insert into bulktest3 (z) values ('ZZZ');";

success = [db executeStatements:sql];

sql = @"select count(*) as count from bulktest1;"
       "select count(*) as count from bulktest2;"
       "select count(*) as count from bulktest3;";

success = [self.db executeStatements:sql withResultBlock:^int(NSDictionary *dictionary) {
    NSInteger count = [dictionary[@"count"] integerValue];
    XCTAssertEqual(count, 1, @"expected one record for dictionary %@", dictionary);
    return 0;
}];

數據處理#

你必須使用標準的SQLite的標準語法,像下面那樣(而不是SQL中那樣):

INSERT INTO myTable VALUES (?, ?, ?, ?)

‘膜眠?’這個符號表示插入數據的替代符岩臣,操作方法會接收參數來替代這個符號 (或者是代表這些參數的,比如說:NSArray, NSDictionary, va_list).

OC中你可以像下面這樣使用:

NSInteger identifier = 42;
NSString *name = @"Liam O'Flaherty (\"the famous Irish author\")";
NSDate *date = [NSDate date];
NSString *comment = nil;

BOOL success = [db executeUpdate:@"INSERT INTO authors (identifier, name, date, comment) VALUES (?, ?, ?, ?)", @(identifier), name, date, comment ?: [NSNull null]];
if (!success) {
    NSLog(@"error = %@", [db lastErrorMessage]);
}

Note:這里需要注意的是宵膨,如果是基本數據類型比如說NSInteger架谎,你需要轉化為NSNumber,你可以使用[ NSNumber numberwithint:identifier]這樣的語法或者是標識符語法:@(identifier)辟躏。
如果是插入nil谷扣,那么你不能直接插入nil,而是需要插入[NSNull null]捎琐,像上面那個例子中寫的是:comment ?: [NSNull null]会涎,那么如果commit是nil的話則會插入nil,反之則會插入commit.

下面這種書寫方法和上面表達的是同一個意思瑞凑。

INSERT INTO authors (identifier, name, date, comment) VALUES (:identifier, :name, :date, :comment)

像上面這種寫法參數是以冒號開頭的末秃,SQLite支持其他字符,但是在字典中key都是以冒號為前綴的籽御,所以你的字典key中不要包含冒號

NSDictionary *arguments = @{@"identifier": @(identifier), @"name": name, @"date": date, @"comment": comment ?: [NSNull null]};
BOOL success = [db executeUpdate:@"INSERT INTO authors (identifier, name, date, comment) VALUES (:identifier, :name, :date, :comment)" withParameterDictionary:arguments];
if (!success) {
    NSLog(@"error = %@", [db lastErrorMessage]);
}

最關鍵的一點就是:千萬不要用NSString的方法比如說<code> stringWithFormat</code>來手動插入參數练慕,必須要使用Values(?,?)這樣的方法篱蝇,把贺待?當作替代符徽曲。(不要自作聰明)

FMDatabaseQueue 是線程安全的

不要在多個線程之間使用同一個<code> FMDatabase</code>對象零截,最好是每一個線程都有一個獨立的<code> FMDatabase</code>對象,如果你在多線程之間使用同一個對象秃臣,那么會有不好的事情發(fā)生查排,你的app可能會經常崩潰,甚至有隕石落下來砸到你的MAC.(文檔里就是這么唬人的)
如果你需要在多線程中使用<code> FMDatabase</code>,那么請使用<code> FMDatabaseQueue</code>奥吩,下面是他的使用方法:
(其實如果我們查看FMDB的源代碼我們可以發(fā)現其實FMDB在后臺也是使用GCD來實現的)

首先創(chuàng)建你的線程

FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithPath:aPath];

然后罐旗,像這樣使用:

[queue inDatabase:^(FMDatabase *db) {
    [db executeUpdate:@"INSERT INTO myTable VALUES (?)", @1];
    [db executeUpdate:@"INSERT INTO myTable VALUES (?)", @2];
    [db executeUpdate:@"INSERT INTO myTable VALUES (?)", @3];

    FMResultSet *rs = [db executeQuery:@"select * from foo"];
    while ([rs next]) {
        …
    }
}];

An easy way to wrap things up in a transaction can be done like this:

[queue inTransaction:^(FMDatabase *db, BOOL *rollback) {
    [db executeUpdate:@"INSERT INTO myTable VALUES (?)", @1];
    [db executeUpdate:@"INSERT INTO myTable VALUES (?)", @2];
    [db executeUpdate:@"INSERT INTO myTable VALUES (?)", @3];

    if (whoopsSomethingWrongHappened) {
        *rollback = YES;
        return;
    }
    // etc…
    [db executeUpdate:@"INSERT INTO myTable VALUES (?)", @4];
}];
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市盈魁,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖偎捎,帶你破解...
    沈念sama閱讀 211,884評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異序攘,居然都是意外死亡茴她,警方通過查閱死者的電腦和手機,發(fā)現死者居然都...
    沈念sama閱讀 90,347評論 3 385
  • 文/潘曉璐 我一進店門程奠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來丈牢,“玉大人,你說我怎么就攤上這事瞄沙〖号妫” “怎么了?”我有些...
    開封第一講書人閱讀 157,435評論 0 348
  • 文/不壞的土叔 我叫張陵距境,是天一觀的道長申尼。 經常有香客問我,道長垫桂,這世上最難降的妖魔是什么晶姊? 我笑而不...
    開封第一講書人閱讀 56,509評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮伪货,結果婚禮上们衙,老公的妹妹穿的比我還像新娘。我一直安慰自己碱呼,他們只是感情好蒙挑,可當我...
    茶點故事閱讀 65,611評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著愚臀,像睡著了一般忆蚀。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上姑裂,一...
    開封第一講書人閱讀 49,837評論 1 290
  • 那天馋袜,我揣著相機與錄音,去河邊找鬼舶斧。 笑死欣鳖,一個胖子當著我的面吹牛,可吹牛的內容都是我干的茴厉。 我是一名探鬼主播泽台,決...
    沈念sama閱讀 38,987評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼什荣,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了怀酷?” 一聲冷哼從身側響起稻爬,我...
    開封第一講書人閱讀 37,730評論 0 267
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎蜕依,沒想到半個月后桅锄,有當地人在樹林里發(fā)現了一具尸體,經...
    沈念sama閱讀 44,194評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡样眠,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,525評論 2 327
  • 正文 我和宋清朗相戀三年竞滓,在試婚紗的時候發(fā)現自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片吹缔。...
    茶點故事閱讀 38,664評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡商佑,死狀恐怖,靈堂內的尸體忽然破棺而出厢塘,到底是詐尸還是另有隱情茶没,我是刑警寧澤,帶...
    沈念sama閱讀 34,334評論 4 330
  • 正文 年R本政府宣布晚碾,位于F島的核電站抓半,受9級特大地震影響,放射性物質發(fā)生泄漏格嘁。R本人自食惡果不足惜笛求,卻給世界環(huán)境...
    茶點故事閱讀 39,944評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望糕簿。 院中可真熱鬧探入,春花似錦、人聲如沸懂诗。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,764評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽殃恒。三九已至植旧,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間离唐,已是汗流浹背病附。 一陣腳步聲響...
    開封第一講書人閱讀 31,997評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留亥鬓,地道東北人完沪。 一個月前我還...
    沈念sama閱讀 46,389評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像贮竟,于是被迫代替她去往敵國和親丽焊。 傳聞我的和親對象是個殘疾皇子较剃,可洞房花燭夜當晚...
    茶點故事閱讀 43,554評論 2 349

推薦閱讀更多精彩內容