Github 地址: FMDB
由于 SQLite3 使用比較麻煩, 所以推薦使用第三方開(kāi)源庫(kù)FMDB , 他以 OC 的方式封裝了SQLite 的C 語(yǔ)言 API.
優(yōu)點(diǎn):
- 使用起來(lái)更加面向?qū)ο?,省去了很多麻煩、冗余的C語(yǔ)言代碼
- 對(duì)比蘋(píng)果自帶的Core Data框架,更加輕量級(jí)和靈活
- 提供了多線程安全的數(shù)據(jù)庫(kù)操作方法,有效地防止數(shù)據(jù)混亂
核心類(lèi)
FMDB有三個(gè)主要的類(lèi):
- FMDatabase
一個(gè)FMDatabase對(duì)象就代表一個(gè)單獨(dú)的SQLite數(shù)據(jù)庫(kù),用來(lái)執(zhí)行SQL語(yǔ)句
- FMResultSet
使用FMDatabase執(zhí)行查詢后的結(jié)果集
- FMDatabaseQueue
用于在多線程中執(zhí)行多個(gè)查詢或更新,它是線程安全的
打開(kāi)數(shù)據(jù)庫(kù)
和c語(yǔ)言框架一樣,F(xiàn)MDB通過(guò)指定SQLite數(shù)據(jù)庫(kù)文件路徑來(lái)創(chuàng)建FMDatabase對(duì)象,但FMDB更加容易理解,使用起來(lái)更容易,使用之前一樣需要導(dǎo)入sqlite3.dylib
。打開(kāi)數(shù)據(jù)庫(kù)方法如下:
NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject stringByAppendingPathComponent:@"person.db"];
FMDatabase *database = [FMDatabase databaseWithPath:path];
if (![database open]) {
NSLog(@"數(shù)據(jù)庫(kù)打開(kāi)失??!");
}
path
的值可以傳入以下三種:
- 具體文件路徑,如果不存在會(huì)自動(dòng)創(chuàng)建
- 空字符串@"",會(huì)在臨時(shí)目錄創(chuàng)建一個(gè)空的數(shù)據(jù)庫(kù),當(dāng)FMDatabase連接關(guān)閉時(shí),數(shù)據(jù)庫(kù)文件也被刪除
- nil,會(huì)創(chuàng)建一個(gè)內(nèi)存中臨時(shí)數(shù)據(jù)庫(kù),當(dāng)FMDatabase連接關(guān)閉時(shí),數(shù)據(jù)庫(kù)會(huì)被銷(xiāo)毀
更新
在FMDB中,除查詢以外的所有操作,都稱(chēng)為“更新”, 如:create
、drop
、insert
、update
、delete
等操作,使用executeUpdate:
方法執(zhí)行更新:
//常用方法有以下3種:
- (BOOL)executeUpdate:(NSString*)sql, ...
- (BOOL)executeUpdateWithFormat:(NSString*)format, ...
- (BOOL)executeUpdate:(NSString*)sql withArgumentsInArray:(NSArray *)arguments
//示例
[database executeUpdate:@"CREATE TABLE IF NOT EXISTS t_person(id integer primary key autoincrement, name text, age integer)"];
//或者
[database executeUpdate:@"INSERT INTO t_person(name, age) VALUES(?, ?)", @"Bourne", [NSNumber numberWithInt:42]];
查詢
- (FMResultSet *)executeQuery:(NSString*)sql, ...
- (FMResultSet *)executeQueryWithFormat:(NSString*)format, ...
- (FMResultSet *)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray *)arguments
例:
FMResultSet *result = [database executeQuery:@"SELECT * FROM t_person"];
//2.遍歷結(jié)果集
while ([result next]) {
NSString *name = [result stringForColumn:@"name"];
int age = [result intForColumn:@"age"];
}
CocoaPods
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
多條語(yǔ)句與批處理
可以將多條語(yǔ)句放在一起執(zhí)行
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;
}];
使用FMDataBaseQueue 和線程安全
不要讓多個(gè)線程分享同一個(gè)FMDatabase實(shí)例,它無(wú)法在多個(gè)線程中同時(shí)使用。 如果在多個(gè)線程中同時(shí)使用一個(gè)FMDatabase實(shí)例,會(huì)造成數(shù)據(jù)混亂等問(wèn)題。所以,請(qǐng)使用 FMDatabaseQueue,它是線程安全的。
- 創(chuàng)建隊(duì)列
FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithPath:aPath];
- 使用隊(duì)列
[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ù)里
[queue inTransaction:^(FMDatabase *database, BOOL *rollback) {
[database executeUpdate:@"INSERT INTO t_person(name, age) VALUES (?, ?)", @"Bourne_1", [NSNumber numberWithInt:1]];
[database executeUpdate:@"INSERT INTO t_person(name, age) VALUES (?, ?)", @"Bourne_2", [NSNumber numberWithInt:2]];
[database executeUpdate:@"INSERT INTO t_person(name, age) VALUES (?, ?)", @"Bourne_3", [NSNumber numberWithInt:3]];
FMResultSet *result = [database executeQuery:@"select * from t_person"];
while([result next]) {
}
//回滾
*rollback = YES;
}];
FMDatabaseQueue 后臺(tái)會(huì)建立系列化的G-C-D隊(duì)列,并執(zhí)行你傳給G-C-D隊(duì)列的塊。這意味著 你從多線程同時(shí)調(diào)用調(diào)用方法,GDC也會(huì)按它接收的塊的順序來(lái)執(zhí)行。