iOS FMDB使用與緩存數(shù)據(jù)

一、FMDB簡介

  • 什么是FMDB
    • FMDB是iOS平臺(tái)SQLite數(shù)據(jù)庫 框架
    • FMDB以面向OC的方式封裝了SQLite的C語言API
  • FMDB優(yōu)點(diǎn)

    • 使用起來更加面向?qū)ο螅∪チ撕芏嗦闊┣堆浴⑷哂嗟腃語言代碼
    • 對(duì)比蘋果自帶的Core Data框架蝠检,更加輕量級(jí)和靈活
    • 提供了多線程安全的數(shù)據(jù)庫操作方法怖亭,有效地防止數(shù)據(jù)混亂
  • FMDB的github地址

  • 核心類

    • FMDatabase
    • 一個(gè)FMDatabase對(duì)象就代表一個(gè)單獨(dú)的SQLite數(shù)據(jù)庫
    • 用來執(zhí)行SQL語句
    • FMDatabase這個(gè)類是線程不安全的涎显,如果在多個(gè)線程中同時(shí)使用一個(gè)FMDatabase實(shí)例,會(huì)造成數(shù)據(jù)混亂等問題
    • 為了保證線程安全兴猩,F(xiàn)MDB提供方便快捷的FMDatabaseQueue類
    • FMResultSet
    • 使用FMDatabase執(zhí)行查詢后的結(jié)果集
    • FMDatabaseQueue
    • 用于在多線程中執(zhí)行多個(gè)查詢或更新期吓,它是線程安全的
// FMDatabaseQueue的創(chuàng)建
FMDatabaseQueue *queue = 

[FMDatabaseQueue databaseQueueWithPath:path];
[queue inDatabase:^(FMDatabase*db) {
   [db executeUpdate:@"INSERTINTO t_student(name)VALUES (?)",@"Jack"];
   [db executeUpdate:@"INSERTINTO t_student(name)VALUES (?)",@"Rose"];
   [db executeUpdate:@"INSERTINTO t_student(name)VALUES (?)",@"Jim"];
   
   FMResultSet*rs= [dbexecuteQuery:@"select* from t_student"];
   while([rsnext]) {
        // …
   }
}];
    ```

+ 注意:打開數(shù)據(jù)庫,文件路徑有三種情況
    - 具體文件路徑:如果不存在會(huì)自動(dòng)創(chuàng)建
    - 空字符串@"":會(huì)在臨時(shí)目錄創(chuàng)建一個(gè)空的數(shù)據(jù)庫倾芝,當(dāng)FMDatabase連接關(guān)閉時(shí)讨勤,數(shù)據(jù)庫文件也被刪除‘ 
    - nil:會(huì)創(chuàng)建一個(gè)內(nèi)存中臨時(shí)數(shù)據(jù)庫,當(dāng)FMDatabase連接關(guān)閉時(shí)晨另,數(shù)據(jù)庫會(huì)被銷毀

  ```objc
// 通過指定SQLite數(shù)據(jù)庫文件路徑來創(chuàng)建FMDatabase對(duì)象
FMDatabase*db= [FMDatabase databaseWithPath:path];
if (![dbopen]) {
   NSLog(@"數(shù)據(jù)庫打開失斕肚А!");
}
  • 在FMDB中借尿,除查詢以外的所有操作刨晴,都稱為“更新”:create、drop路翻、insert狈癞、update、delete等
// 使用executeUpdate:方法執(zhí)行更新
- (BOOL)executeUpdate:(NSString*)sql, ...
- (BOOL)executeUpdateWithFormat:(NSString*)format,...
- (BOOL)executeUpdate:(NSString*)sqlwithArgumentsInArray:(NSArray*)arguments
  • 執(zhí)行查詢
- (FMResultSet*)executeQuery:(NSString*)sql, ...
- (FMResultSet*)executeQueryWithFormat:(NSString*)format,...
- (FMResultSet*)executeQuery:(NSString*)sqlwithArgumentsInArray:(NSArray*)arguments

二茂契、CoreData 與 FMDB

  • coreData:自動(dòng)生成SQL語句蝶桶,不能自己設(shè)置,個(gè)性化的掉冶、優(yōu)化的等SQL語句不能實(shí)現(xiàn)
  • FMDB:可以自己定制不同的SQL語句真竖,擴(kuò)展性好

三、FMDB實(shí)例 基本使用

  • 1.導(dǎo)入FMDB框架到項(xiàng)目中
  • 2.使用FMDB


# 二厌小、增刪改查
+ 數(shù)據(jù)庫操作都放在queue中的疼邀,queue中操作database,線程是安全的

```objc
#import "JPViewController.h"
#import "FMDB.h"

@interface JPViewController ()
@property (nonatomic, strong) FMDatabase *db;
@end

@implementation JPViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    // 1.獲得數(shù)據(jù)庫文件的路徑
    NSString *doc = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
    NSString *filename = [doc stringByAppendingPathComponent:@"students.sqlite"];
    
    // 2.得到數(shù)據(jù)庫
    FMDatabase *db = [FMDatabase databaseWithPath:filename];
    
    // 3.打開數(shù)據(jù)庫
    if ([db open]) {
        // 4.創(chuàng)表
        BOOL result = [db executeUpdate:@"CREATE TABLE IF NOT EXISTS t_student (id integer PRIMARY KEY AUTOINCREMENT, name text NOT NULL, age integer NOT NULL);"];
        if (result) {
            NSLog(@"成功創(chuàng)表");
        } else {
            NSLog(@"創(chuàng)表失敗");
        }
    }
    
    self.db = db;
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    [self delete];
    [self insert];
    [self query];
}

- (void)insert
{
    for (int i = 0; i<10; i++) {
        NSString *name = [NSString stringWithFormat:@"jack-%d", arc4random_uniform(100)];
        // executeUpdate : 不確定的參數(shù)用?來占位召锈,參數(shù)都必須是對(duì)象
        [self.db executeUpdate:@"INSERT INTO t_student (name, age) VALUES (?, ?);", name, @(arc4random_uniform(40))];
//        [self.db executeUpdate:@"INSERT INTO t_student (name, age) VALUES (?, ?);" withArgumentsInArray:@[name, @(arc4random_uniform(40))]]; // ?方式旁振,參數(shù)要是對(duì)象,不是對(duì)象要包裝為對(duì)象
        
        // executeUpdateWithFormat : format方式拼接涨岁,不確定的參數(shù)用%@拐袜、%d等來占位
//        [self.db executeUpdateWithFormat:@"INSERT INTO t_student (name, age) VALUES (%@, %d);", name, arc4random_uniform(40)];
// 注意:如果直接寫%@不用加單引號(hào)兩邊,會(huì)自動(dòng)加單引號(hào)兩邊梢薪,如果'jack_%d'的兩邊不加單引號(hào)就會(huì)報(bào)錯(cuò)蹬铺,所以要注意
     [self.db executeUpdateWithFormat:@"INSERT INTO t_student (name, age) VALUES ('jack_%d', %d);", name, arc4random_uniform(40)]; 
    }
}

- (void)delete
{
//    [self.db executeUpdate:@"DELETE FROM t_student;"];
    [self.db executeUpdate:@"DROP TABLE IF EXISTS t_student;"];
    [self.db executeUpdate:@"CREATE TABLE IF NOT EXISTS t_student (id integer PRIMARY KEY AUTOINCREMENT, name text NOT NULL, age integer NOT NULL);"];
}

- (void)query
{
    // 1.執(zhí)行查詢語句
    FMResultSet *resultSet = [self.db executeQuery:@"SELECT * FROM t_student"];
    
    // 2.遍歷結(jié)果
    while ([resultSet next]) {
        int ID = [resultSet intForColumn:@"id"];
        NSString *name = [resultSet stringForColumn:@"name"];
        int age = [resultSet intForColumn:@"age"];
        NSLog(@"%d %@ %d", ID, name, age);
    }
}
@end
+  事務(wù)操作

```objc
#import "JPViewController.h"
#import "FMDB.h" // 導(dǎo)入頭文件

@interface JPViewController ()
@property (nonatomic, strong) FMDatabaseQueue *queue;
@end

@implementation JPViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    // 1.獲得數(shù)據(jù)庫文件的路徑
    NSString *doc = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
    NSString *filename = [doc stringByAppendingPathComponent:@"persons.sqlite"];
    
    // 2.得到數(shù)據(jù)庫
    FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithPath:filename];
    
    // 3.打開數(shù)據(jù)庫
    [queue inDatabase:^(FMDatabase *db) {
        // 4.創(chuàng)表
        BOOL result = [db executeUpdate:@"CREATE TABLE IF NOT EXISTS t_person (id integer PRIMARY KEY AUTOINCREMENT, name text NOT NULL, age integer NOT NULL);"];
        if (result) {
            NSLog(@"成功創(chuàng)表");
        } else {
            NSLog(@"創(chuàng)表失敗");
        }
    }];
    
    self.queue = queue;
}

// 事務(wù)操作:錯(cuò)誤會(huì)回滾
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
//    [self.queue inDatabase:^(FMDatabase *db) {
//        [db beginTransaction];
//        
//        [db executeUpdate:@"INSERT INTO t_person (name, age) VALUES (?, ?);", @"jake", @30];
//        [db executeUpdate:@"INSERT INTO t_person (name, age) VALUES (?, ?);", @"jake", @30];
//        [db executeUpdate:@"INSERT INTO t_person (name, age) VALUES (?, ?);", @"jake", @30];
//        [db executeUpdate:@"INSERT INTO t_person (name, age) VALUES (?, ?);", @"jake", @30];
//        
//        [db commit];
//    }];
    
//    [self.queue inTransaction:^(FMDatabase *db, BOOL *rollback) {
//        [db executeUpdate:@"INSERT INTO t_person (name, age) VALUES (?, ?);", @"jake", @30];
//        [db executeUpdate:@"INSERT INTO t_person (name, age) VALUES (?, ?);", @"jake", @30];
//        [db executeUpdate:@"INSERT INTO t_person (name, age) VALUES (?, ?);", @"jake", @30];
//        [db executeUpdate:@"INSERT INTO t_person (name, age) VALUES (?, ?);", @"jake", @30];
//    }];
    
  
}
@end

四、FMDB緩存數(shù)據(jù)

#import "JPStatusTool.h"
#import "JPHttpTool.h"
#import "MJExtension.h"
#import "FMDB.h"
#import "JPStatus.h"

@implementation JPStatusTool

/** 數(shù)據(jù)庫實(shí)例 */
static FMDatabase *_db;

+ (void)initialize
{
    // 1.獲得數(shù)據(jù)庫文件的路徑
    NSString *doc = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
    NSString *filename = [doc stringByAppendingPathComponent:@"status.sqlite"];
    
    // 2.得到數(shù)據(jù)庫
    _db = [FMDatabase databaseWithPath:filename];
    
    // 3.打開數(shù)據(jù)庫
    if ([_db open]) {
        // 4.創(chuàng)表
        BOOL result = [_db executeUpdate:@"CREATE TABLE IF NOT EXISTS t_home_status (id integer PRIMARY KEY AUTOINCREMENT, access_token text NOT NULL, status_idstr text NOT NULL, status_dict blob NOT NULL);"];
        if (result) {
            NSLog(@"成功創(chuàng)表");
        } else {
            NSLog(@"創(chuàng)表失敗");
        }
    }
}

+ (void)homeStatusesWithParam:(JPHomeStatusesParam *)param success:(void (^)(JPHomeStatusesResult *))success failure:(void (^)(NSError *))failure
{
//    param.count = @5;
    
    // 從數(shù)據(jù)庫中讀缺病(加載)緩存數(shù)據(jù)(微博模型數(shù)組)
    NSArray *cachedHomeStatuses = [self cachedHomeStatusesWithParam:param];
    if (cachedHomeStatuses.count != 0) { // 有緩存數(shù)據(jù)
        if (success) {
            JPHomeStatusesResult *result = [[JPHomeStatusesResult alloc] init];
            result.statuses = cachedHomeStatuses;
            success(result);
        }
    } else { // 沒有緩存數(shù)據(jù)
        NSDictionary *params = [param keyValues];
        
        [JPHttpTool get:@"https://api.weibo.com/2/statuses/home_timeline.json" params:params success:^(id responseObj) {
            // 新浪返回的字典數(shù)組
            NSArray *statusDictArray = responseObj[@"statuses"];
            
            // 緩存微博字典數(shù)組
            [self saveHomeStatusDictArray:statusDictArray accessToken:param.access_token];
            
            if (success) {
                JPHomeStatusesResult *result = [JPHomeStatusesResult objectWithKeyValues:responseObj];
                success(result);
            }
        } failure:^(NSError *error) {
            if (failure) {
                failure(error);
            }
        }];
    }
}

/**
 *  通過請(qǐng)求參數(shù)去數(shù)據(jù)庫中加載對(duì)應(yīng)的微博數(shù)據(jù)
 *
 *  @param param 請(qǐng)求參數(shù)
 */
+ (NSArray *)cachedHomeStatusesWithParam:(JPHomeStatusesParam *)param
{
    // 創(chuàng)建數(shù)組緩存微博數(shù)據(jù)
    NSMutableArray *statuses = [NSMutableArray array];
    
    // 根據(jù)請(qǐng)求參數(shù)查詢數(shù)據(jù)
    FMResultSet *resultSet = nil;
    if (param.since_id) {
        resultSet = [_db executeQuery:@"SELECT * FROM t_home_status WHERE access_token = ? AND status_idstr > ? ORDER BY status_idstr DESC limit ?;", param.access_token, param.since_id, param.count];
    } else if (param.max_id) {
        resultSet = [_db executeQuery:@"SELECT * FROM t_home_status WHERE access_token = ? AND status_idstr <= ? ORDER BY status_idstr DESC limit ?;", param.access_token, param.max_id, param.count];
    } else {
        resultSet = [_db executeQuery:@"SELECT * FROM t_home_status WHERE access_token = ? ORDER BY status_idstr DESC limit ?;", param.access_token, param.count];
    }
    
    // 遍歷查詢結(jié)果
    while (resultSet.next) {
        NSData *statusDictData = [resultSet objectForColumnName:@"status_dict"];
        NSDictionary *statusDict = [NSKeyedUnarchiver unarchiveObjectWithData:statusDictData];
        // 字典轉(zhuǎn)模型
        JPStatus *status = [JPStatus objectWithKeyValues:statusDict];
        // 添加模型到數(shù)組中
        [statuses addObject:status];
    }
    
    return statuses;
}

/**
 *  緩存微博字典數(shù)組到數(shù)據(jù)庫中
 */
+ (void)saveHomeStatusDictArray:(NSArray *)statusDictArray accessToken:(NSString *)accessToken
{
    for (NSDictionary *statusDict in statusDictArray) {
        // 把statusDict字典對(duì)象序列化成NSData二進(jìn)制數(shù)據(jù)
        NSData *data = [NSKeyedArchiver archivedDataWithRootObject:statusDict];
        [_db executeUpdate:@"INSERT INTO t_home_status (access_token, status_idstr, status_dict) VALUES (?, ?, ?);",
         accessToken, statusDict[@"idstr"], data];
    }
}
@end
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末甜攀,一起剝皮案震驚了整個(gè)濱河市秋泄,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌规阀,老刑警劉巖恒序,帶你破解...
    沈念sama閱讀 210,914評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異谁撼,居然都是意外死亡歧胁,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,935評(píng)論 2 383
  • 文/潘曉璐 我一進(jìn)店門厉碟,熙熙樓的掌柜王于貴愁眉苦臉地迎上來喊巍,“玉大人,你說我怎么就攤上這事箍鼓≌覆危” “怎么了?”我有些...
    開封第一講書人閱讀 156,531評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵款咖,是天一觀的道長何暮。 經(jīng)常有香客問我,道長之剧,這世上最難降的妖魔是什么郭卫? 我笑而不...
    開封第一講書人閱讀 56,309評(píng)論 1 282
  • 正文 為了忘掉前任砍聊,我火速辦了婚禮背稼,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘玻蝌。我一直安慰自己蟹肘,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,381評(píng)論 5 384
  • 文/花漫 我一把揭開白布俯树。 她就那樣靜靜地躺著帘腹,像睡著了一般。 火紅的嫁衣襯著肌膚如雪许饿。 梳的紋絲不亂的頭發(fā)上阳欲,一...
    開封第一講書人閱讀 49,730評(píng)論 1 289
  • 那天,我揣著相機(jī)與錄音陋率,去河邊找鬼球化。 笑死,一個(gè)胖子當(dāng)著我的面吹牛瓦糟,可吹牛的內(nèi)容都是我干的筒愚。 我是一名探鬼主播,決...
    沈念sama閱讀 38,882評(píng)論 3 404
  • 文/蒼蘭香墨 我猛地睜開眼菩浙,長吁一口氣:“原來是場噩夢啊……” “哼巢掺!你這毒婦竟也來了句伶?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,643評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤陆淀,失蹤者是張志新(化名)和其女友劉穎考余,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體倔约,經(jīng)...
    沈念sama閱讀 44,095評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡秃殉,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,448評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了浸剩。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片钾军。...
    茶點(diǎn)故事閱讀 38,566評(píng)論 1 339
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖绢要,靈堂內(nèi)的尸體忽然破棺而出吏恭,到底是詐尸還是另有隱情,我是刑警寧澤重罪,帶...
    沈念sama閱讀 34,253評(píng)論 4 328
  • 正文 年R本政府宣布樱哼,位于F島的核電站,受9級(jí)特大地震影響剿配,放射性物質(zhì)發(fā)生泄漏搅幅。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,829評(píng)論 3 312
  • 文/蒙蒙 一呼胚、第九天 我趴在偏房一處隱蔽的房頂上張望茄唐。 院中可真熱鬧,春花似錦蝇更、人聲如沸沪编。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,715評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蚁廓。三九已至,卻和暖如春厨幻,著一層夾襖步出監(jiān)牢的瞬間相嵌,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,945評(píng)論 1 264
  • 我被黑心中介騙來泰國打工况脆, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留饭宾,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,248評(píng)論 2 360
  • 正文 我出身青樓漠另,卻偏偏與公主長得像捏雌,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子笆搓,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,440評(píng)論 2 348

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