FMDB v2.6.2
這是個SQLite的OC封裝。SQLite的地址:http://sqlite.org/
FMDB郵件名單
http://groups.google.com/group/fmdb
閱讀SQLite的常見問題
http://www.sqlite.org/faq.html
由于FMDB建立在SQLite之上,你想要至上而下讀一遍這個頁面。當(dāng)你在這里,確保你標(biāo)記了SQLite的文檔頁面:http://www.sqlite.org/docs.html
特約Contributing
關(guān)于FMBDB你是不是有什么驚奇的點子呢?你應(yīng)該考慮聯(lián)系ccgus,不過你得確定他并沒有因為一些理由限定了這個點子。另外,pull是隨意的,保持你本地編程的習(xí)慣。然而,即使幾個禮拜也沒有收到來自ccgus的任何訊息也別著急,你可以發(fā)個消息問一下發(fā)生了什么。
CocoaPods
FMDB支持CocoaPods做依賴管理。
如果需要數(shù)據(jù)可加密你需要使用FMDB/SQLCipher.FMDB/SQLCipher聲明了SQLCipher的依賴,允許FMDB通過“-DSQLITE_HAS_CODEC”標(biāo)志去編譯。
FMDB類參考
http://ccgus.github.io/fmdb/html/index.html
ARC還是手動內(nèi)存管理
你可以選擇兩種中任意一種方式,在你的cocoa項目中,F(xiàn)MDB可以在編譯時候跟據(jù)你的選擇編譯正確的code。
用法
FMDB中有三個主要的類
1.FMDatabase-代表一個獨(dú)立的SQLite數(shù)據(jù)庫,執(zhí)行SQL語句。
2.FMResultSet-代表FMDatebase查詢的結(jié)果集
3.FMDatabaseQueue-如果你想要在多線程中查詢和更新,你應(yīng)該使用這個類,
創(chuàng)建數(shù)據(jù)庫
FMDatabase通過一個SQLite數(shù)據(jù)庫文件的路徑創(chuàng)建。這個路徑可以有以下三種樣式:
1.一個系統(tǒng)文件路徑.硬盤上之前不存在的,如果它不存在,F(xiàn)MDB會為你新建。
2.一個空字符串,一個空數(shù)據(jù)庫在臨時文件中創(chuàng)建。在數(shù)據(jù)庫連接關(guān)閉的時候,這個數(shù)據(jù)庫會被刪除。
3.NULL(空)。一個在內(nèi)存中的數(shù)據(jù)庫將被創(chuàng)建。在數(shù)據(jù)庫連接關(guān)閉的時候,這個數(shù)據(jù)庫會被銷毀。
想了解更多關(guān)于臨時或內(nèi)存數(shù)據(jù)庫,請閱讀sqlite文檔:http://www.sqlite.org/inmemorydb.html
eg: FMDatabase *db = [FMDatabase databaseWithpath:@“/tmp/tmp.db”];
打開數(shù)據(jù)庫
和數(shù)據(jù)庫建立連接之前,應(yīng)該確保它是打開的,在內(nèi)存不足、禁止開啟、創(chuàng)建數(shù)據(jù)庫的時候會打開失敗。
if(![db open]){
[db release];
return;
}
執(zhí)行更新
除了SELECT格式的數(shù)據(jù)庫執(zhí)行語句都是更新。包括CREATE,UPDATE,INSERT,ALTER,COMMIT,BEGIN,DETACH,DELETE,DROP,END,EXPLAIN,VACUUM,還有replace語句等等。基本上,只要你的SQL語句不是以SELECT開頭,都是更新語句。
執(zhí)行更新返回一個單一BOOL值,返回值為YES代表執(zhí)行更新成功,返回值為NO表示發(fā)生了一些錯誤。你可以調(diào)用-laseErrorMessage和-laseErrorCode方法接收更多訊息。
執(zhí)行查詢
一個SELECt語句是一個查詢語句并且通過-executrQuery方法執(zhí)行。
執(zhí)行查詢成功返回FMResultSet對象,失敗返回nil。你應(yīng)該使用-laseErrorMessage和-laseErrorCode方法確定到底為什么查詢失敗。
為了循環(huán)訪問查詢結(jié)果集,你可以使用while()循環(huán).你也需要從一個紀(jì)錄到另一條。在FMDB中,最簡單的辦法是這樣:
FMResultSet *s = [db executeQuery:@“SELECT *FROM myTable”];
while ([s next]){
//retrieve values for each record
}
通常你在使用查詢結(jié)果的返回值前必須先調(diào)用-[FMResultSet next],即使你只需要一次,像這樣:
FMResultSet *s = [db executeQuery:@“SELECT COUNT(*) FROM myTable”];
if ([s next]) {
int totalCount = [s intForColumnIndex:0];
}
FMResultSet 擁有許多方法去獲取適當(dāng)類型的數(shù)據(jù)
intForColumn:
longForColumn:
longLongIntForColumn:
boolForColumn:
doubleForColumn:
stringForColumn:
dateForColumn:
dataForColumn:
dataNoCopyForColumn:
UTF8StringForColumnName:
objectForColumnName:
這里的每一個方法都有一個對應(yīng)的{type}ForColumnIndex:表達(dá)式。基于字段在結(jié)果集中的位置可以被用來獲取數(shù)據(jù)
,和字段名一一對應(yīng)。
特別的,你在這里不需要-close一個FMResultSet,直到結(jié)果集都被釋放或者父數(shù)據(jù)庫關(guān)閉了。
關(guān)閉(Closing)
當(dāng)你完成了數(shù)據(jù)的查詢和更新,你應(yīng)該-close這個FMDatabase的連接讓SQLite釋放那些操作過程中占用的資源。
[db close];
Transactions(事務(wù))
FMDatabase可以通過調(diào)用合適的方法或者執(zhí)行開始和結(jié)束事務(wù)型語句開始并提交一個事務(wù)。
多條語句和批量添加
你可以使用FMDatabase的executeStatements:withResultBlock:去做一個多條SQL語句在一個字符串中:
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;
}];
數(shù)據(jù)處理
當(dāng)提供一個SQL語句到FMDB。你應(yīng)該檢查每一個輸入值.代替的,你可以使用標(biāo)準(zhǔn)綁定語法:
INSERT INTO myTable VALUES (?, ?, ?, ?)
?符號在SQLite標(biāo)準(zhǔn)中是一個插入的占位符。執(zhí)行方法全部的接受來自參數(shù)的變量值(或者是這些參數(shù)的,像NSArray,NSDictionary,或者是va_list),適當(dāng)對你隱藏。
還有,在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]);
}
注意:基本數(shù)據(jù)類型,像NSInteger這樣的變量自負(fù),應(yīng)該用MNSNumber對象表示,需要用@語法,像上面那樣。或者用[NSNumber numberWithInt:identifier]語法也可以。
另外,在SQL中NULL值應(yīng)該用[NSNull null]插入.舉個例子,像上面那個comment自斷可能為nil,你可以一使用comment ?: [NSNull null]語法,當(dāng)comment為不為空是插入這個字符串,為空時插入[NSNull null].
在swift中,你應(yīng)該使用executeUpdate(values:),不僅僅是swift的簡明語法,也同樣是swift2的錯誤拋出句柄。
do {
let identifier = 42
let name = "Liam O'Flaherty (\"the famous Irish author\")"
let date = NSDate()
let comment: String? = nil
try db.executeUpdate("INSERT INTO authors (identifier, name, date, comment) VALUES (?, ?, ?, ?)", values: [identifier, name, date, comment ?? NSNull()])
} catch {
print("error = \(error)")
}
注意:在swift,你不需要像OC那樣基礎(chǔ)數(shù)字類型的封裝,但是如果你想要插入一個可變字符串,你將需要使用這樣的語法comment ?? NSNull() (i.e.,如果為nil,使用NSNull,否則使用這個字符串)。
二選一,你可以使用named參數(shù)語法。
INSERT INTO authors (identifier, name, date, comment) VALUES (:identifier, :name, :date, :comment)
這個參數(shù)列表必須以冒號開始,SQLite也支持其它符號,但是內(nèi)部字典的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]);
}
在SQL語句中key應(yīng)該使用NSString的stringWithFormat方法手動插入對應(yīng)的值。那么swift的字符串咋辦?,使用?占位符向數(shù)據(jù)庫插入值(或者在SELECT語句中使用WHERE從句)
使用FMDatabaseQueue 和線程安全
在多線程使用同一個FMDatabase的單例是一個壞主意。通常為每一個線程創(chuàng)建FMDatabase對象都是OK的,千萬不要跨線程使用數(shù)據(jù)庫單例,清楚不要在多線程同時使用。壞的事情終將發(fā)生你將會得到一些閃退,或者一個異常,或者是隕石從天而降砸中了你的mac,這些將會被抱怨。
多線程使用的時候不要實力話單例。
用FMDatabaseQueue替代。實例化一個單例FMDatabaseQueue并且在多線程中使用.這個FMDatabaseQueue對象將通過隊列管理同步執(zhí)行來自多線程的命令。這里是如何使用,第一步,創(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]) {
…
}
}];
簡單的多任務(wù)包裝在一個事務(wù)中key這樣做:
[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];
}];
在swift里這樣:
queue.inTransaction { db, rollback in
do {
try db.executeUpdate("INSERT INTO myTable VALUES (?)", values: [1])
try db.executeUpdate("INSERT INTO myTable VALUES (?)", values: [2])
try db.executeUpdate("INSERT INTO myTable VALUES (?)", values: [3])
if whoopsSomethingWrongHappened {
rollback.memory = true
return
}
try db.executeUpdate("INSERT INTO myTable VALUES (?)", values: [4])
} catch {
rollback.memory = true
print(error)
}
}
FMDatabaseQueue將在序列號的隊列(從類名開始)執(zhí)行塊.因此你可以同時調(diào)用在多線程的多FMDatabaseQueue的方法,在順序排到的時候它們將被執(zhí)行。這個方法查詢和更新并不會擠在一起,就不會出問題了。
注意:FMDatabaseQueue的方式是塊形勢的,因此即使你正在塊中,他們也不會跑在其它線程。
通過塊自定義sqlite函數(shù)
你可以做這個,例如:查看main.m里的 -makeFunctionNamed:
swift
你也可以在Swift項目中使用FMDB
如果是這樣,你必須:
1.從FMDB的src文件下拷貝相關(guān)的.m和.h文件到你的項目中。
你可以拷貝所有的文件(最簡單的方式),或者只拷貝你需要的。最基本的你需要FMDatabase和FMResultSet。FMDatabaseAddition提供一些非常方便好用的方法,因此你可能也需要它。如果你要在多線程任務(wù)中使用數(shù)據(jù)庫,F(xiàn)MdatabaseQueue也是非常有用的。如果你選擇不要拷貝所有src文件目錄下的文件,你還需要更新FMDB.h,這是與你項目的唯一連接。
注意,如果你拷貝了所有文件到你的項目中(推薦做法),你可能想要拖動個人文件到你的項目中,不是文件夾,因為如果你拖動文件夾,你將得不到關(guān)于添加橋引頭文件(bridging header)的提示(看下一點)
2.如果你被提示創(chuàng)建橋引頭文件,你應(yīng)該創(chuàng)建它。如果你沒有收到提示且之前并沒有橋引頭文件,你應(yīng)該去添加一個。
更多關(guān)于橋引頭文件,看swift和oc混編( Swift and Objective-C in the Same Project.)
3.在你的橋引頭文件中,添加這一行。
#import "FMDB.h"
4.在try模式下使用executeQuery和executeUpdate的變量表達(dá)式帶上sql和values參數(shù),就像下面的代碼一樣,這些executeQuery和executeUpdate的譯文都會拋出對應(yīng)的異常在swift2中。
如果你按照上面的做了,你可以用FMDatabase寫swift代碼,舉例:
let documents = try! NSFileManager.defaultManager().URLForDirectory(.DocumentDirectory, inDomain: .UserDomainMask, appropriateForURL: nil, create: false)
let fileURL = documents.URLByAppendingPathComponent("test.sqlite")
let database = FMDatabase(path: fileURL.path)
if !database.open() {
print("Unable to open database")
return
}
do {
try database.executeUpdate("create table test(x text, y text, z text)", values: nil)
try database.executeUpdate("insert into test (x, y, z) values (?, ?, ?)", values: ["a", "b", "c"])
try database.executeUpdate("insert into test (x, y, z) values (?, ?, ?)", values: ["e", "f", "g"])
let rs = try database.executeQuery("select x, y, z from test", values: nil)
while rs.next() {
let x = rs.stringForColumn("x")
let y = rs.stringForColumn("y")
let z = rs.stringForColumn("z")
print("x = \(x); y = \(y); z = \(z)")
}
} catch let error as NSError {
print("failed: \(error.localizedDescription)")
}
database.close()
歷史
歷史和變化總結(jié)在GitHub Page的CHANGES_AND_TODO_LIST.txt里可見。
貢獻(xiàn)者
FMDB的貢獻(xiàn)者名單在Contributors.txt”中
在項目中使用FMDB,你應(yīng)該有志于成為一個有辨識力的開發(fā)者
FMDBMigrationManager,一個FMDBSQLite架構(gòu)遷移管理系統(tǒng)
https://github.com/layerhq/FMDBMigrationManager
FCModel,一個對操作SQL易取可選擇的核心數(shù)據(jù)模型。
https://github.com/marcoarment/FCModel
FMDB開源的代碼風(fēng)格
空格,不要tab鍵。方括號,不要打點語法。FMDB的早就在執(zhí)行方括號格式,一直保持這樣的風(fēng)格。
報告bug
編碼時盡量減少bug。你需要讓開發(fā)者很簡單的發(fā)現(xiàn)你的bug并且復(fù)現(xiàn)。如果有用,裝作那個可以有效解決bug的人支持著3個主要項目。為一些開源項目工作,有一個新生的寶寶,通常時候非常非常忙。
我們曾經(jīng)添加模版函數(shù)在main.m(FMDBReportABugFunction)在FMDB發(fā)布版本幫你解決。
通過Xcode打開fmdb項目
打開main.m函數(shù)修改FMDBReportABugFunction重現(xiàn)。
設(shè)置你的表在代碼中。
確認(rèn)你的查詢或者是更新
添加一些關(guān)于這個bug的斷言
然后你可以把這些通過郵件發(fā)給FMDB郵件表。或者,你可以紀(jì)錄這個bug通過githubFMDBbug記錄員。
可選的:
解決這個bug,發(fā)送附件到郵件表,確保所有其它測試在你的修改運(yùn)行之后。
支持
這個FMDB支持通道是一個郵件地址列表(上面有),bug提在這里,或者在Stack Overflow。所以就是說,社區(qū)和志愿組織提供支持。
FMDB開發(fā)是監(jiān)視。如果FMDB對你有幫助,考慮采購一個應(yīng)用FM或者告訴你的朋友關(guān)于它。
執(zhí)照
FMDB的許可證在"License.txt”文件中