FMDB 使用進(jìn)階

數(shù)據(jù)庫(kù).jpg

目錄:

  • 類文件解讀
  • 更多你不知道的API
  • 關(guān)于事物的使用
  • 數(shù)據(jù)庫(kù)升級(jí)

只要是擼過(guò)一遍FMDB的人肌蜻,基本都知道怎么用,增蒋搜、刪、改酸休、查祷杈,偶爾涉及多表操作,多條件查詢宿刮,搜搜sql語(yǔ)句私蕾,基本上沒(méi)什么問(wèn)題,但是你以為這就夠了么磕潮?我曾經(jīng)也是這樣以為的容贝。

簡(jiǎn)單問(wèn)一下:

1、除了執(zhí)行斤富、更新满力,你還用到FMDB中的哪些API(例如:批處理)
2轻纪、FMDB中事物的代碼實(shí)現(xiàn)方法?
3叠纷、數(shù)據(jù)庫(kù)升級(jí)是怎么操作的?
4我擂、va_list是什么東西缓艳?

如果你全都知道看峻,那么可以略過(guò)這篇文章,如果你和我當(dāng)初一樣溪窒,只知道封裝個(gè)增冯勉、刪、改宛瞄、查方法交胚,我建議最好還是讀一下


一、類文件

文件名 描述
FMDatabase 一個(gè)FMDatabase對(duì)象就代表一個(gè)單獨(dú)的SQLite數(shù)據(jù)庫(kù)
FMResultSet 使用FMDatabase執(zhí)行查詢后的結(jié)果集合
FMDatabaseQueue 用于在多線程中執(zhí)行多個(gè)查詢或更新杯活,它是線程安全的
FMDatabaseAdditions 新增對(duì)查詢結(jié)果只返回單個(gè)值的方法進(jìn)行簡(jiǎn)化旁钧,對(duì)表互拾、列是否存在,版本號(hào)彤委,校驗(yàn)SQL等等功能

二或衡、更多你不知道的API

很長(zhǎng)時(shí)間以來(lái)我只知道這兩個(gè)方法车遂,一個(gè)更新舶担,一個(gè)執(zhí)行彬呻,夠用啦

db executeQuery:<#(nonnull NSString *), ...#>
db executeUpdate:<#(nonnull NSString *), ...#>
  • 更新(create、drop剪况、insert蒲跨、update、delete)
  • 執(zhí)行(select)

直到有一天沒(méi)事兒的時(shí)候看了看源碼孙咪,才發(fā)現(xiàn)巡语,雖然這個(gè)庫(kù)很輕量級(jí),但是我看到的依舊是冰山一角荤堪,接下來(lái)讓我看看它還有哪些能讓我眼前一亮的東西理澎。

FMDatabase主要API解讀

查詢

- (FMResultSet * _Nullable)executeQuery:(NSString*)sql, ...;
- (FMResultSet * _Nullable)executeQueryWithFormat:(NSString*)format, ... NS_FORMAT_FUNCTION(1,2);
- (FMResultSet * _Nullable)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray *)arguments;
- (FMResultSet * _Nullable)executeQuery:(NSString *)sql values:(NSArray * _Nullable)values error:(NSError * _Nullable __autoreleasing *)error;
- (FMResultSet * _Nullable)executeQuery:(NSString *)sql withParameterDictionary:(NSDictionary * _Nullable)arguments;
- (FMResultSet * _Nullable)executeQuery:(NSString *)sql withVAList:(va_list)args;
更新

- (BOOL)executeUpdate:(NSString*)sql, ...;
- (BOOL)executeUpdateWithFormat:(NSString *)format, ... NS_FORMAT_FUNCTION(1,2);
- (BOOL)executeUpdate:(NSString*)sql withArgumentsInArray:(NSArray *)arguments;
- (BOOL)executeUpdate:(NSString*)sql values:(NSArray * _Nullable)values error:(NSError * _Nullable __autoreleasing *)error;
- (BOOL)executeUpdate:(NSString*)sql withParameterDictionary:(NSDictionary *)arguments;
- (BOOL)executeUpdate:(NSString*)sql withVAList: (va_list)args;

方法解讀

1糠爬、該方法的參數(shù)必須是對(duì)象执隧,不能為基礎(chǔ)數(shù)據(jù)類型,否則就會(huì)崩潰
- (BOOL)executeUpdate:(NSString*)sql, ...;

錯(cuò)誤寫法:?
[_dataBaseQueue inDatabase:^(FMDatabase * _Nonnull db) {
    [db executeUpdate:@"INSERT INTO usertable VALUES (?, ? , ?)", 1, @"lizhiqiang", 25];
}];



2、如果需要插入基礎(chǔ)數(shù)據(jù)類型峦嗤,要么自己做一下轉(zhuǎn)換屋摔,要么調(diào)用以下方法
- (FMResultSet * _Nullable)executeQueryWithFormat:(NSString*)format, ... NS_FORMAT_FUNCTION(1,2);

[_dataBaseQueue inDatabase:^(FMDatabase * _Nonnull db) {
    [db open];
    [db executeUpdateWithFormat:@"INSERT INTO usertable VALUES (%d, %@ , %d)", 1, @"lizhiqiang", 25];
    [db close];
}];


3、這個(gè)沒(méi)什么可說(shuō)的装黑,數(shù)組參數(shù),直接上代碼
- (BOOL)executeUpdate:(NSString*)sql withArgumentsInArray:(NSArray *)arguments;

[_dataBaseQueue inDatabase:^(FMDatabase * _Nonnull db) {
    [db open];
    [db executeUpdate:@"INSERT INTO usertable VALUES (?, ? , ?)" withArgumentsInArray:@[@2, @"yanghuixue", @26]];
    [db close];
}];


4糠睡、比方法3多了error指針參數(shù)疚颊,記錄更新失敗
- (BOOL)executeUpdate:(NSString*)sql values:(NSArray * _Nullable)values error:(NSError * _Nullable __autoreleasing *)error;


5、注意均抽,這個(gè)mark一下母截,參數(shù)為字典清寇,寫法變了护蝶,并且插入字段必須與字典key相對(duì)應(yīng)
- (BOOL)executeUpdate:(NSString*)sql withParameterDictionary:(NSDictionary *)arguments;

NSDictionary *testDict = @{
                           @"id" : @14,
                           @"name" : @"ly",
                           @"age" : @15
                           };
[_dataBaseQueue inDatabase:^(FMDatabase * _Nonnull db) {
    [db open];
    [db executeUpdate:@"INSERT INTO usertable VALUES(:id, :name, :age)" withParameterDictionary:testDict];
    [db close];
}];


6、va_list是C語(yǔ)言中解決變參問(wèn)題的一組宏
- (BOOL)executeUpdate:(NSString*)sql withVAList: (va_list)args;

關(guān)于va_list盔夜,在后續(xù)更新中會(huì)提到堤魁,這里先不做過(guò)多闡述

批處理:可以通過(guò)調(diào)用executeStatements方法妥泉,一次執(zhí)行多個(gè)sql語(yǔ)句

API:
- (BOOL)executeStatements:(NSString *)sql;
- (BOOL)executeStatements:(NSString *)sql withResultBlock:(__attribute__((noescape)) FMDBExecuteStatementsCallbackBlock _Nullable)block;

例:
NSString *creatSqlString = @"CREATE TABLE IF NOT EXISTS grouptable(id INTEGER, gcid VARCHAR(64), gcname VARCHAR(64));"
@"CREATE TABLE IF NOT EXISTS usertable(id INTEGER, name VARCHAT(1024), age INTEGER)";

[_dataBaseQueue inDatabase:^(FMDatabase *db) {
    [db open];
    [db executeStatements:creatSqlString];
    [db close];
}];

事物:(詳情見:三、關(guān)于事物的使用)

- (BOOL)beginTransaction;
- (BOOL)beginDeferredTransaction;
- (BOOL)beginImmediateTransaction;
- (BOOL)beginExclusiveTransaction;
- (BOOL)commit;
- (BOOL)rollback;

三蝇率、關(guān)于事物的使用

在數(shù)據(jù)庫(kù)中刽沾,事務(wù)可以保證數(shù)據(jù)操作的完整性。當(dāng)存在大量并發(fā)操作锅尘,容易出現(xiàn)死鎖問(wèn)題布蔗。在SQLite中忙菠,為了解決該問(wèn)題牛欢,提供三種事務(wù)模式

3.1淆游、事物模式
  • Exclusive
  • Deferred
  • Immediate
typedef NS_ENUM(NSInteger, FMDBTransaction) {
    // 事務(wù)開始執(zhí)行,就獲取EXCLUSIVE鎖拾稳,此時(shí)腊脱,其他連接無(wú)法進(jìn)行任何讀寫操作
    FMDBTransactionExclusive,

    // 事務(wù)開始執(zhí)行時(shí),不預(yù)先獲取任何鎖悍抑。當(dāng)進(jìn)行讀操作杜耙,獲取SHARED LOCK鎖佑女;當(dāng)進(jìn)行第一次寫操作,獲取RESERVED鎖
    FMDBTransactionDeferred,

    // 事務(wù)開始執(zhí)行摸吠,就獲取RESERVED鎖嚎花。這時(shí),其他連接只能進(jìn)行讀操作
    FMDBTransactionImmediate,
};
3.2轿腺、延時(shí)性事務(wù)和獨(dú)占性事務(wù)的區(qū)別:

在SQLite 3.0.8或更高版本中丛楚,事務(wù)可以是延遲的,即時(shí)的或者獨(dú)占的仿荆。
“延遲的”即是說(shuō)在數(shù)據(jù)庫(kù)第一次被訪問(wèn)之前不獲得鎖。 這樣就會(huì)延遲事務(wù)锦亦,BEGIN語(yǔ)句本身不做任何事情令境。直到初次讀取或訪問(wèn)數(shù)據(jù)庫(kù)時(shí)才獲取鎖舔庶。對(duì)數(shù)據(jù)庫(kù)的初次讀取創(chuàng)建一個(gè)SHARED鎖 ,初次寫入創(chuàng)建一個(gè)RESERVED鎖惕橙。由于鎖的獲取被延遲到第一次需要時(shí)弥鹦,別的線程或進(jìn)程可以在當(dāng)前線程執(zhí)行BEGIN語(yǔ)句之后創(chuàng)建另外的事務(wù) 寫入數(shù)據(jù)庫(kù)。
若事務(wù)是即時(shí)的朦促,則執(zhí)行BEGIN命令后立即獲取RESERVED鎖苍鲜,而不等數(shù)據(jù)庫(kù)被使用玷犹。在執(zhí)行BEGIN IMMEDIATE之后歹颓, 你可以確保其它的線程或進(jìn)程不能寫入數(shù)據(jù)庫(kù)或執(zhí)行BEGIN IMMEDIATE或BEGIN EXCLUSIVE. 但其它進(jìn)程可以讀取數(shù)據(jù)庫(kù)。 獨(dú)占事務(wù)在所有的數(shù)據(jù)庫(kù)獲取EXCLUSIVE鎖领跛,在執(zhí)行BEGIN EXCLUSIVE之后撤奸,你可以確保在當(dāng)前事務(wù)結(jié)束前沒(méi)有任何其它線程或進(jìn)程 能夠讀寫數(shù)據(jù)庫(kù)

API

- (void)inTransaction:(__attribute__((noescape)) void (^)(FMDatabase *db, BOOL *rollback))block
- (void)inDeferredTransaction:(__attribute__((noescape)) void (^)(FMDatabase *db, BOOL *rollback))block
- (void)inExclusiveTransaction:(__attribute__((noescape)) void (^)(FMDatabase *db, BOOL *rollback))block
- (void)inImmediateTransaction:(__attribute__((noescape)) void (^)(FMDatabase * _Nonnull, BOOL * _Nonnull))block

當(dāng)我們直接調(diào)用inTransaction胧瓜、beginTransaction操作時(shí),使用EXCLUSIVE模式蒲肋,適合數(shù)據(jù)庫(kù)讀寫較少的情況;
當(dāng)使用beginDefferedTransaction方法兜粘,則使用DEFFERED模式孔轴,適合讀寫頻繁的場(chǎng)景

3.3、FMDB中事物使用的兩種方式
  • 直接開啟事物
  • 在inData中開啟事物
[_dataBaseQueue inTransaction:^(FMDatabase * _Nonnull db, BOOL * _Nonnull rollback) {
    [db open];
    BOOL isDeleteGroupSuccess = [db executeUpdate:@"DELETE FROM grouptable WHERE gcid = ?", groupID];
    BOOL isDeleteMembershipSuccess = [db executeUpdate:@"DELETE FROM groupshiptable WHERE gcid = ?", groupID];
    if (!isDeleteGroupSuccess || !isDeleteMembershipSuccess) {
        // 當(dāng)對(duì)兩個(gè)表的操作中玄窝,其中一個(gè)失敗悍引,數(shù)據(jù)回滾
        *rollback = YES;
        return;
    }
    [db close];
}];
[_dataBaseQueue inDatabase:^(FMDatabase * _Nonnull db) {
    [db open];
    // 開啟事物
    [db beginTransaction];
    BOOL isDeleteGroupSuccess = [db executeUpdate:@"DELETE FROM grouptable WHERE gcid = ?", groupID];
    BOOL isDeleteMembershipSuccess = [db executeUpdate:@"DELETE FROM groupshiptable WHERE gcid = ?", groupID];
    if (!isDeleteGroupSuccess || !isDeleteMembershipSuccess) {
        // 當(dāng)對(duì)兩個(gè)表的操作中趣斤,其中一個(gè)失敗,數(shù)據(jù)回滾
        [db rollback];
        return;
    }
    // 提交事物
    [db commit];
    [db close];
}];

看過(guò)原碼的人應(yīng)該都知道玉凯,其實(shí)他們是一樣的联贩,inTransaction默認(rèn)調(diào)用事物的FMDBTransactionExclusive類型,

switch (transaction) {
    case FMDBTransactionExclusive:
        // inTransaction其實(shí)就是直接調(diào)用    ?? ?? 
        [[self database] beginTransaction];     
        break;
    case FMDBTransactionDeferred:
        [[self database] beginDeferredTransaction];
        break;
    case FMDBTransactionImmediate:
        [[self database] beginImmediateTransaction];
        break;
}

四盲厌、數(shù)據(jù)庫(kù)升級(jí)常見的三種方式

  • 記錄版本號(hào)方式
  • 根據(jù)新增字段是否存在方式
  • 表遷移方式
4.1吗浩、版本號(hào)方式

廢話少說(shuō)没隘,直接上代碼

// 1、靜態(tài)寫死當(dāng)前版本號(hào)阀湿,手動(dòng)更新
static const NSInteger kCurrentDBVersion = 1;

- (void)checkDataBaseUpgrade {
    // 2瑰妄、獲取舊的版本號(hào)翰撑,與當(dāng)前版本號(hào)對(duì)比
    NSString *oldDBVersion = [[NSUserDefaults standardUserDefaults] objectForKey:dbVersionKey];
    if ([oldDBVersion integerValue] < kCurrentDBVersion) {
        // 3啊央、如果本次存儲(chǔ)的版本號(hào) < 當(dāng)前版本號(hào)涨醋,數(shù)據(jù)庫(kù)升級(jí)浴骂,更新本地存儲(chǔ)
        [self upgrade:[oldDBVersion integerValue]];
        [[NSUserDefaults standardUserDefaults] setObject:@(kCurrentDBVersion) forKey:dbVersionKey];
        [[NSUserDefaults standardUserDefaults] synchronize];
    }
}

// 4、遞歸的方式更新
- (void)upgrade:(NSInteger)oldVersion {
    if (oldVersion >= kCurrentDBVersion) {
        return;
    }
    
    switch (oldVersion) {
        case 1: {  // version1 -> version2
            [self upgradeFromFirstToSecond];
        }
            break;
        case 2: {  // version2 -> version3
            [self upgradeFromSecondToThird];
        }
            break;
            // ...
        default:
            break;
    }
    oldVersion ++;
    [self upgrade:oldVersion];
}

// 5趣苏、在需要添加的表格中增加字段
- (void)upgradeFromFirstToSecond {
    //  ALTER TABLE table_name ADD column_name datatype
}

- (void)upgradeFromSecondToThird {
    //  ALTER TABLE table_name ADD column_name datatype
}
4.2梯轻、檢測(cè)字段是否存在方式

在FMDatabaseAdditions類中調(diào)用columnExists方法用于檢測(cè)喳挑,傳入需要檢測(cè)的字段名和表名

- (BOOL)columnExists:(NSString*)columnName inTableWithName:(NSString*)tableName;

具體操作為:

+ (void)checkDataBaseUpdate {
    FMDatabaseQueue *dataBaseQueue = [FMDatabaseQueue databaseQueueWithPath:DATABASEPATH];
    [dataBaseQueue inDatabase:^(FMDatabase *db) {
        [db open];
        if ([db columnExists:@"relation" inTableWithName:@"Profile"]) {
            // 如果Profile表中存在relation這個(gè)字段,不需要操作
        } else {
            // 如果Profile表中不存在relation這個(gè)字段单绑,插入列
            [db executeUpdate:@"ALTER TABLE Profile ADD relation text"];
        }
        [db close];
    }];
}
4.3曹宴、表遷移

第三種方式比較復(fù)雜笛坦,忘了當(dāng)時(shí)在哪里看到了,講的邏輯就是

  • 重新創(chuàng)建一個(gè)表
  • 將舊表的數(shù)據(jù)導(dǎo)入到新的表中
  • 刪除舊表

作為一個(gè)拓展吧蜗帜,以后有時(shí)間的話再研究资厉,這里提供一個(gè)思路


參考文章:

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末宴偿,一起剝皮案震驚了整個(gè)濱河市诀豁,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌娩践,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,185評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異吨岭,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)旦事,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門姐浮,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)葬馋,“玉大人,你說(shuō)我怎么就攤上這事扫尖÷永” “怎么了?”我有些...
    開封第一講書人閱讀 163,524評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)悦污。 經(jīng)常有香客問(wèn)我切端,道長(zhǎng),這世上最難降的妖魔是什么踏枣? 我笑而不...
    開封第一講書人閱讀 58,339評(píng)論 1 293
  • 正文 為了忘掉前任茵瀑,我火速辦了婚禮,結(jié)果婚禮上竞帽,老公的妹妹穿的比我還像新娘。我一直安慰自己疙渣,他們只是感情好抱虐,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,387評(píng)論 6 391
  • 文/花漫 我一把揭開白布恳邀。 她就那樣靜靜地躺著,像睡著了一般刷钢。 火紅的嫁衣襯著肌膚如雪乳附。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,287評(píng)論 1 301
  • 那天阱缓,我揣著相機(jī)與錄音荆针,去河邊找鬼颁糟。 笑死,一個(gè)胖子當(dāng)著我的面吹牛玖媚,可吹牛的內(nèi)容都是我干的婚脱。 我是一名探鬼主播,決...
    沈念sama閱讀 40,130評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼涡贱,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了督函?” 一聲冷哼從身側(cè)響起激挪,我...
    開封第一講書人閱讀 38,985評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤垄分,失蹤者是張志新(化名)和其女友劉穎薄湿,沒(méi)想到半個(gè)月后偷卧,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,420評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡坐求,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,617評(píng)論 3 334
  • 正文 我和宋清朗相戀三年桥嗤,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了仔蝌。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,779評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖豆混,靈堂內(nèi)的尸體忽然破棺而出皿伺,到底是詐尸還是另有隱情,我是刑警寧澤鸵鸥,帶...
    沈念sama閱讀 35,477評(píng)論 5 345
  • 正文 年R本政府宣布妒穴,位于F島的核電站,受9級(jí)特大地震影響杰赛,放射性物質(zhì)發(fā)生泄漏矮台。R本人自食惡果不足惜根时,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,088評(píng)論 3 328
  • 文/蒙蒙 一蛤迎、第九天 我趴在偏房一處隱蔽的房頂上張望含友。 院中可真熱鬧窘问,春花似錦、人聲如沸南缓。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)逗威。三九已至岔冀,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間罐呼,已是汗流浹背侦高。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工奉呛, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人瞧壮。 一個(gè)月前我還...
    沈念sama閱讀 47,876評(píng)論 2 370
  • 正文 我出身青樓咆槽,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親济欢。 傳聞我的和親對(duì)象是個(gè)殘疾皇子赠堵,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,700評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • ORA-00001: 違反唯一約束條件 (.) 錯(cuò)誤說(shuō)明:當(dāng)在唯一索引所對(duì)應(yīng)的列上鍵入重復(fù)值時(shí)小渊,會(huì)觸發(fā)此異常法褥。 O...
    我想起個(gè)好名字閱讀 5,311評(píng)論 0 9
  • MYSQL 基礎(chǔ)知識(shí) 1 MySQL數(shù)據(jù)庫(kù)概要 2 簡(jiǎn)單MySQL環(huán)境 3 數(shù)據(jù)的存儲(chǔ)和獲取 4 MySQL基本操...
    Kingtester閱讀 7,811評(píng)論 5 116
  • 關(guān)于Mongodb的全面總結(jié) MongoDB的內(nèi)部構(gòu)造《MongoDB The Definitive Guide》...
    中v中閱讀 31,930評(píng)論 2 89
  • 我不會(huì)像一只受傷的猴子,逢人就看自己的痛處酬屉。我要做一只微笑的向日葵半等,有著自己的驕傲。
    騖遠(yuǎn)閱讀 253評(píng)論 0 0
  • 初秋之后,天氣轉(zhuǎn)涼谬擦,很多體質(zhì)虛弱的老人和孩子很容易患上感冒。其實(shí)感冒是一種自愈性很強(qiáng)的疾病惨远,如 果身體強(qiáng)壯的話谜悟,只...
    養(yǎng)生食刻閱讀 829評(píng)論 0 0