iOS數(shù)據(jù)庫(kù)存儲(chǔ)之SQLite3

資源連接:

iOS數(shù)據(jù)庫(kù)存儲(chǔ)之SQL語(yǔ)句;

iOS數(shù)據(jù)存儲(chǔ)之文件沙盒;

iOS數(shù)據(jù)存儲(chǔ)之NSCoding;

SQLite常見(jiàn)問(wèn)題;

數(shù)據(jù)庫(kù)簡(jiǎn)介

數(shù)據(jù)庫(kù)(Database)是按照數(shù)據(jù)結(jié)構(gòu)來(lái)組織、存儲(chǔ)和管理數(shù)據(jù)的倉(cāng)庫(kù)
數(shù)據(jù)庫(kù)可以分為2大種類(lèi):關(guān)系型數(shù)據(jù)庫(kù)(主流)和 對(duì)象型數(shù)據(jù)庫(kù)

常用關(guān)系型數(shù)據(jù)庫(kù)
PC端:Oracle、MySQL、SQL Server、Access、DB2、Sybase
嵌入式\移動(dòng)客戶(hù)端:SQLite

數(shù)據(jù)庫(kù)存儲(chǔ)單元

  1. 數(shù)據(jù)庫(kù)的存儲(chǔ)結(jié)構(gòu)和excel很像,以表(table)為單位。
  2. 數(shù)據(jù)庫(kù)存儲(chǔ)數(shù)據(jù)的步驟
  • 新建一張表(table)
  • 添加多個(gè)字段(column,列,屬性)
  • 添加多行記錄(row,record,每行存放多個(gè)字段對(duì)應(yīng)的值)

SQLite3

overview:SQLite是一款輕型的嵌入式數(shù)據(jù)庫(kù)
它占用資源非常的低,在嵌入式設(shè)備中,可能只需要幾百K的內(nèi)存就夠了
它的處理速度比Mysql、PostgreSQL這兩款著名的數(shù)據(jù)庫(kù)都還快。目前iOS系統(tǒng)為開(kāi)發(fā)者提供的數(shù)據(jù)庫(kù)是SQLite3版本。

iOS 提供的數(shù)據(jù)庫(kù)接口是C語(yǔ)言接口,用起來(lái)比較麻煩,下面我們重點(diǎn)介紹面向?qū)ο蟮腇MDB。

FMDB

一款優(yōu)秀的三方開(kāi)源的SQLite操作框架,支持OC和Swift,上方資源鏈接有其GitHub地址,本文主要來(lái)自其README.Markdown。FMDB早已經(jīng)支持CocoaPods,可以用 pod 'FMDB' 進(jìn)行安裝。你可以用在ARC和MRC項(xiàng)目中,因?yàn)镕MDB已經(jīng)為我們?cè)诰幾g階段處理好了。

主要結(jié)構(gòu)

有三個(gè)主要的類(lèi):

  1. FMDatabase: 為執(zhí)行SQL語(yǔ)句(statements)提供了一個(gè)單一的數(shù)據(jù)庫(kù)。
  2. FMResultSet :執(zhí)行查詢(xún)(query,這里FMDB特指,下面會(huì)有解釋?zhuān)┱Z(yǔ)句的結(jié)果集。
  3. FMDatabaseQueue:在多線程中執(zhí)行查詢(xún)(queries)和更行(updates),需要用到,因?yàn)樗€程安全。

數(shù)據(jù)庫(kù)創(chuàng)建

MDatabase 是通過(guò)一個(gè) SQLite 數(shù)據(jù)庫(kù)文件路徑創(chuàng)建的,此路徑可以是以下三者之一:

  1. 一個(gè)文件的系統(tǒng)路徑,如果此文件不存在,它會(huì)為你在此路徑下創(chuàng)建文件
  2. 一個(gè)空的字符串 @""。會(huì)在臨時(shí)位置創(chuàng)建一個(gè)空的數(shù)據(jù)庫(kù),當(dāng) FMDatabase 連接關(guān)閉時(shí),該數(shù)據(jù)庫(kù)會(huì)被刪除。
  3. NULL。會(huì)在內(nèi)存中創(chuàng)建一個(gè)數(shù)據(jù)庫(kù),當(dāng)FMDatabase 連接關(guān)閉時(shí),該數(shù)據(jù)庫(kù)會(huì)被銷(xiāo)毀。
NSString *path = [NSTemporaryDirectory() stringByAppendingPathComponent:@"tmp.db"];
FMDatabase *db = [FMDatabase databaseWithPath:path];

For more infomation

打開(kāi)數(shù)據(jù)庫(kù)

數(shù)據(jù)庫(kù)必須是打開(kāi)狀態(tài),才能與之交互。如果沒(méi)有足夠的資源和權(quán)限來(lái)打開(kāi)創(chuàng)建數(shù)據(jù)庫(kù),數(shù)據(jù)庫(kù)會(huì)打開(kāi)失敗。

if (![db open]) {
    // [db release];   // uncomment this line in manual referencing code; in ARC, this is not necessary/permitted
    db = nil;
    return;
}

Updates 和 Queries

  1. SQL語(yǔ)句(statement)只要不是SELECT語(yǔ)句,你都應(yīng)該使用FMDB的update方法,這些SQL語(yǔ)句包括CREATE, UPDATE, INSERT, ALTER, COMMIT, BEGIN, DETACH, DELETE, DROP, END, EXPLAIN, VACUUM, and REPLACE,只要你的語(yǔ)句不以SELECT開(kāi),就用update方法吧。

  2. 執(zhí)行update方法后會(huì)返回一個(gè) BOOL 值,返回 YES 表示執(zhí)行更新語(yǔ)句成功,返回 NO 表示出現(xiàn)錯(cuò)誤,可以通過(guò)調(diào)用 -lastErrorMessage 和 -lastErrorCode 方法獲取更多錯(cuò)誤信息。

  3. 執(zhí)行query方法后,如果成功會(huì)返回一個(gè) FMResultSet 對(duì)象,反之會(huì)返回 nil。通過(guò) -lastErrorMessage 和 -lastErrorCode 方法可以確定為什么會(huì)查詢(xún)失敗。

Queries

為了遍歷查詢(xún)結(jié)果,需要 while() 循環(huán),然后逐條記錄查看。在 FMDB 中,可以通過(guò)下面的簡(jiǎn)單方式實(shí)現(xiàn):

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

FMResultSet 提供了很多方便的方法來(lái)查詢(xún)數(shù)據(jù):
intForColumn:
longForColumn:
longLongIntForColumn:
boolForColumn:
doubleForColumn:
stringForColumn:
dateForColumn:
dataForColumn:
dataNoCopyForColumn:
UTF8StringForColumn:
objectForColumn:

不用字段名也可以查詢(xún),F(xiàn)MDB提供了一中通過(guò)列號(hào)獲取相關(guān)值的變體方法 ForColumnIndex:

特別說(shuō)明,一個(gè) FMResultSet 沒(méi)有必要手動(dòng) -close,因?yàn)榻Y(jié)果集合 (result set) 被釋放或者源數(shù)據(jù)庫(kù)關(guān)閉會(huì)自動(dòng)關(guān)閉。

關(guān)閉數(shù)據(jù)庫(kù)

當(dāng)對(duì)數(shù)據(jù)庫(kù)進(jìn)行查詢(xún)和更新操作完成后,需要調(diào)用 -close 關(guān)閉數(shù)據(jù)庫(kù) FMDatabase 的連接。

[db close];

事務(wù)

FMDatabase 可以通過(guò)調(diào)用方法來(lái)開(kāi)始和提交事務(wù),也可以通過(guò)執(zhí)行開(kāi)始\結(jié)束事務(wù) (begin\end transaction) 語(yǔ)句。

多語(yǔ)句和批處理

在一句話(huà)中執(zhí)行多個(gè)SQL語(yǔ)句

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ù)類(lèi)型處理

當(dāng)使用FMDB處理SQL語(yǔ)句時(shí),你應(yīng)該使用標(biāo)準(zhǔn)的SQLite語(yǔ)法,不應(yīng)該有額外處理。先看張圖:

數(shù)據(jù)庫(kù)參數(shù)語(yǔ)法.png

通過(guò)圖我們可以看出盡管SQL語(yǔ)句大體相似,但是在不同數(shù)據(jù)庫(kù)的特點(diǎn),可能參數(shù)化SQL語(yǔ)句不同,例如在Access中參數(shù)化SQL語(yǔ)句是在參數(shù)直接以“?”作為參數(shù)名,在SQL Server中是參數(shù)有“@”前綴,在MySQL中是參數(shù)有“?”前綴,在Oracle中參數(shù)以“:”為前綴。

這里SQLite的參數(shù)化語(yǔ)法與Access相同,如下代碼。

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:基本數(shù)據(jù)類(lèi)型應(yīng)該被包裝成OC對(duì)象。

當(dāng)然你也可以使用命名參數(shù)語(yǔ)法,例如:INSERT INTO authors (identifier, name, date, comment) VALUES (:identifier, :name, :date, :comment);看下面一段代碼:

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

Note:字典里面的key值,不應(yīng)該加上:號(hào)了,因?yàn)镕MDB已經(jīng)幫你做了。

FMDatabaseQueue 和 Thread Safety

Note: 在多線程中用單一的FMDatabase實(shí)例不是好的做法,因?yàn)樗皇蔷€程安全的。作為替代方案,可以用FMDatabaseQueue實(shí)例。

  1. 第一步,創(chuàng)建實(shí)例
FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithPath:aPath];

  1. 這么用
[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]) {
       …
   }
}];

  1. 事物處理

    [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 ...
    

}];

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容