IOS數據存儲之FMDB數據庫


前言:

    最近幾天一直在折騰數據庫存儲,之前文章(http://www.cnblogs.com/whoislcj/p/5485959.html)介紹了Sqlite 數據庫,SQLite是一種小型的輕量級的關系型數據庫,不過直接用ios sdk提供的API來進行數據庫開發,多多少少感覺不那么得心應手。后來也學了更加面向對象的CoreData數據庫,不過coreData感覺對數據庫的支持不太那么好,雖然操作方便,但是損失了性能以及效率,對於數據量比較大的app來說就有點不太合適了,如果有興趣的可以看下之前有關coreData的文章(http://www.cnblogs.com/whoislcj/p/5488024.html)。今天來學習一下對於IOS數據庫封裝比較好的數據庫第三方開源庫FMDB,他對sqlite sdk API進行了二次封裝,直接使用OC來訪問,讓使用變得更方便,更熟悉。

FMDB 源碼托管地址:

   https://github.com/ccgus/fmdb

FMDB使用常用類:

   FMDatabase : 一個單一的SQLite數據庫,用於執行SQL語句。
   FMResultSet :執行查詢一個FMDatabase結果集,這個和Android的Cursor類似。
   FMDatabaseQueue :在多個線程來執行查詢和更新時會使用這個類。

作為一名實用主義開發者的我一般希望大家能夠把我的代碼復制到項目稍作修改就能使用,秉着這個理念,為此我特意寫了一個FMDB數據庫管理類FMDBManager:數據庫的創建,打開,關閉,升級,數據的增刪改查,以及事務的開啟和開啟事務的好處。


接下來看下
FMDBManager具體代碼實現:

FMDBManager.h

#import <Foundation/Foundation.h>

@interface FMDBManager : NSObject<NSCopying>

//創建數據庫管理者單例
+(instancetype)shareManager;

//創建數據庫
-(void)createDb;

//打開數據庫
-(BOOL)openDb;

//關閉數據庫
-(BOOL)closeDb;

//創建數據庫表
-(void)creatTable;

//插入數據
-(void)insertData:(NSString*)tempName;

//插入數據未開啟事務
-(void)insertDataByNomarl:(NSArray*)tempNames;

//插入數據開啟事務
-(void)insertDataByTransaction:(NSArray*)tempNames;

//刪除數據
-(void)deleteData:(NSString*)tempName;

//刪除數據
-(void)deleteData;

//修改數據
-(void)updateData:(NSString*)tempName;

//查詢數據
-(void)queryData;

@end

FMDBManager.m

#import "FMDBManager.h"
#import "FMDatabase.h"

#define DBNAME @"fbdb_test"
#define TBNAME @"persons" //表名
#define DBVERSION 1      //數據庫版本
#define DBVERSIONKEY @"dbversion_key" //存儲數據庫版本key

static FMDBManager *shareManager=nil;

@implementation FMDBManager
{
    FMDatabase *db;
}

-(instancetype)init
{
    self=[super init];
    if (self) {
        [self createDb];
        [self creatTable];
        [self upgrade];
    }
    return self;
}
//創建數據庫管理者單例
+(instancetype)shareManager
{
    if(shareManager==nil){
        @synchronized(self){
            if(shareManager==nil){
                shareManager =[[[self class]alloc]init];
            }
        }
    }
    return shareManager;
}

-(id)copyWithZone:(NSZone *)zone
{
    
    return shareManager;
}

+(id)allocWithZone:(struct _NSZone *)zone
{
    if(shareManager==nil){
        shareManager =[super allocWithZone:zone];
    }
    return shareManager;
}

//檢查數據庫是否需要升級
- (void)upgrade {
    //獲取存儲好的原版本號
    NSInteger oldVersionNum = [[NSUserDefaults standardUserDefaults] integerForKey:DBVERSIONKEY];
    if (DBVERSION <= oldVersionNum || oldVersionNum == 0) {
        return;
    }
    
    //升級
    [self upgrade:oldVersionNum];
    
    // 保存新的版本號到庫中 -這里大家可以使用NSUserDefault存儲
    [[NSUserDefaults standardUserDefaults]setInteger:DBVERSION forKey:DBVERSIONKEY];
}

//根據不同版本執行不同的升級邏輯
- (void)upgrade:(NSInteger)oldVersion {
    //對比數據庫版本
    if (oldVersion >= DBVERSION) {
        return;
    }
    switch (oldVersion) {
        case 0:
            //執行相應的升級操作
            break;
        case 1:
            //執行相應的升級操作
            break;
        case 2:
            //執行相應的升級操作
            break;
        default:
            break;
    }
    oldVersion ++;
    // 遞歸判斷是否需要升級
    [self upgrade:oldVersion];
}

//創建數據庫
-(void)createDb
{
    NSString *doc=[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
    NSString *fileName=[doc stringByAppendingPathComponent:DBNAME];
    db = [FMDatabase databaseWithPath:fileName];
}

//打開數據庫
-(BOOL)openDb
{
    return [db open];
}

//關閉數據庫
-(BOOL)closeDb
{
    return  [db close];
}

//創建數據庫表
-(void)creatTable
{
    [self openDb];
    NSString *creatTableSql=[NSString stringWithFormat:@"create table if not exists %@(id integer primary key,name text)",TBNAME];
    
    BOOL result=[db executeUpdate:creatTableSql];
    if(result){
        NSLog(@"創建表成功");
    }else{
        NSLog(@"創建表失敗");
    }
    [self closeDb];
}

//插入數據
-(void)insertData:(NSString*)tempName
{
    [self openDb];
    NSString *insertSql = [NSString stringWithFormat:@"insert into %@ (name) values ('%@')",TBNAME,tempName];
    BOOL result=[db executeUpdate:insertSql];
    if(result){
        NSLog(@"插入數據成功");
    }else{
        NSLog(@"插入數據失敗");
    }
    [self closeDb];
}

//插入數據未開啟事務
-(void)insertDataByNomarl:(NSArray*)tempNames;
{
    [self openDb];
    for(NSString *name in tempNames){
        NSString *insertSql = [NSString stringWithFormat:@"insert into %@ (name) values ('%@')",TBNAME,name];
        BOOL result=[db executeUpdate:insertSql];
        if(result){
            //NSLog(@"插入數據成功");
        }else{
           // NSLog(@"插入數據失敗");
        }
    }
    [self closeDb];
}

//插入數據開啟事務
-(void)insertDataByTransaction:(NSArray*)tempNames{
    [self openDb];
    [db beginTransaction];
    BOOL isRollBack = NO;
    @try {
        for(NSString *name in tempNames){
            NSString *insertSql = [NSString stringWithFormat:@"insert into %@ (name) values ('%@')",TBNAME,name];
            BOOL result=[db executeUpdate:insertSql];
            if(result){
                //NSLog(@"插入數據成功");
            }else{
               // NSLog(@"插入數據失敗");
            }
        }
    }
    @catch (NSException *exception) {
        isRollBack = YES;
        [db rollback];
    }
    @finally {
        if (!isRollBack) {
            [db commit];
        }
    }
    [self closeDb];
}

//刪除數據
-(void)deleteData:(NSString*)tempName
{
    [self openDb];
    NSString *deleteSql=[NSString stringWithFormat:@"delete from %@ where name = '%@'",TBNAME,tempName];
    BOOL result=[db executeUpdate:deleteSql];
    if(result){
        NSLog(@"刪除數據成功");
    }else{
        NSLog(@"刪除數據失敗");
    }
    [self closeDb];
}

//刪除數據
-(void)deleteData
{
    [self openDb];
    NSString *deleteSql=[NSString stringWithFormat:@"delete from %@ ",TBNAME];
    BOOL result=[db executeUpdate:deleteSql];
    if(result){
        NSLog(@"刪除數據成功");
    }else{
        NSLog(@"刪除數據失敗");
    }
    [self closeDb];
}

//修改數據
-(void)updateData:(NSString*)tempName
{
    [self openDb];
    NSString *updateSql=[NSString stringWithFormat:@"update %@ set name ='test' where name = '%@'",TBNAME,tempName];
    BOOL result=[db executeUpdate:updateSql];
    if(result){
        NSLog(@"更新數據成功");
    }else{
        NSLog(@"更新數據失敗");
    }
    [self closeDb];
}

//查詢數據
-(void)queryData
{
    [self openDb];
    NSString *querySql =[NSString stringWithFormat:@"select * from %@",TBNAME];
    FMResultSet *resultSet = [db executeQuery:querySql];
    
    // 2.遍歷結果
    while ([resultSet next]) {
        int ID = [resultSet intForColumn:@"id"];
        NSString *name = [resultSet stringForColumn:@"name"];
        NSLog(@"Id = %d name= %@ ",ID,name);
    }
    
    [self closeDb];
}

以上是具體實現,接下來看下怎么使用:

             //插入100條數據
              for(int i=0;i<10;i++){
                  NSString *string = [[NSString alloc] initWithFormat:@"%d",i];
                  [[FMDBManager shareManager]insertData:string];
              }
              //然后查詢一下
              [[FMDBManager shareManager]queryData];
              //然后刪除一條數據
              [[FMDBManager shareManager]deleteData:@"5"];
              //更新數據
              [[FMDBManager shareManager]updateData:@"3"];
              //然后查詢一下
              [[FMDBManager shareManager]queryData];
              //刪除數據
              [[FMDBManager shareManager]deleteData];
              //然后查詢一下
              [[FMDBManager shareManager]queryData];

看了調用方式 是不是覺得很簡單,瞬間有沒有感覺源碼寫代碼竟然可以如此愜意!當然注重代碼質量的我們應該更見注重代碼的執行效率,就像一家裝修很豪華的飯店但是飯菜做的很難吃一樣,再美好的東西也變得然並卵了,接下來我看下FMDB有沒封裝過導致效率下降了呢?為此我准備測試一下批量插入操作,為了對比我直接拿之前同樣是10000條數據的Sqlite結果來對比。

FMDB 未開啟事務

//插入數據未開啟事務
-(void)insertDataByNomarl:(NSArray*)tempNames;
{
    [self openDb];
    for(NSString *name in tempNames){
        NSString *insertSql = [NSString stringWithFormat:@"insert into %@ (name) values ('%@')",TBNAME,name];
        BOOL result=[db executeUpdate:insertSql];
        if(result){
            //NSLog(@"插入數據成功");
        }else{
           // NSLog(@"插入數據失敗");
        }
    }
    [self closeDb];
}

FMDB 開啟事務

//插入數據開啟事務
-(void)insertDataByTransaction:(NSArray*)tempNames{
    [self openDb];
    [db beginTransaction];
    BOOL isRollBack = NO;
    @try {
        for(NSString *name in tempNames){
            NSString *insertSql = [NSString stringWithFormat:@"insert into %@ (name) values ('%@')",TBNAME,name];
            BOOL result=[db executeUpdate:insertSql];
            if(result){
                //NSLog(@"插入數據成功");
            }else{
               // NSLog(@"插入數據失敗");
            }
        }
    }
    @catch (NSException *exception) {
        isRollBack = YES;
        [db rollback];
    }
    @finally {
        if (!isRollBack) {
            [db commit];
        }
    }
    [self closeDb];
}

測試程序

    //測試事務
    NSMutableArray *testArray =[[NSMutableArray alloc]init];
    int testMaxCount =10000;
    for(int i=0;i<testMaxCount;i++){
        NSString *string = [[NSString alloc] initWithFormat:@"%d",i];
        [testArray addObject:string];
    }
    
    //未開啟事務插入
    CFAbsoluteTime start = CFAbsoluteTimeGetCurrent();
    
    [[FMDBManager shareManager]insertDataByNomarl:testArray];
    CFAbsoluteTime end=CFAbsoluteTimeGetCurrent();
    NSLog(@"普通插入 time cost: %0.3f", end - start);
    
    //刪除數據
    [[FMDBManager shareManager]deleteData];
    
    //開啟事務插入
    start = CFAbsoluteTimeGetCurrent();
    
    [[FMDBManager shareManager]insertDataByTransaction:testArray];
    
    end=CFAbsoluteTimeGetCurrent();
    NSLog(@"開啟事務插入 time cost: %0.3f", end - start);

運行結果:測試數據 10000條 執行時間單位 秒

   開啟事務:0.050

為開啟事務:5.058 

 

那么之前的sqlite呢?

開啟事務耗時:0.049

未開啟事務耗時:5.614

哈哈,上面的對比結果一目了然,FMDB既保證了執行的效率,又方便的開發,真是IOS開發工程師的一大利器。

 

對比了效率,問題來了:對於數據庫升級支持的怎么樣呢?

其實這個很簡單,讓人出乎意料又在情理之中,那是因為它的升級和sqlite方案一樣一樣的!小二!直接上代碼!

檢查升級:

-(instancetype)init
{
    self=[super init];
    if (self) {
        [self createDb];
        [self creatTable];
        [self upgrade];//檢查升級
    }
    return self;
}

具體升級邏輯:

//檢查數據庫是否需要升級
- (void)upgrade {
    //獲取存儲好的原版本號
    NSInteger oldVersionNum = [[NSUserDefaults standardUserDefaults] integerForKey:DBVERSIONKEY];
    if (DBVERSION <= oldVersionNum || oldVersionNum == 0) {
        return;
    }
    
    //升級
    [self upgrade:oldVersionNum];
    
    // 保存新的版本號到庫中 -這里大家可以使用NSUserDefault存儲
    [[NSUserDefaults standardUserDefaults]setInteger:DBVERSION forKey:DBVERSIONKEY];
}

//根據不同版本執行不同的升級邏輯
- (void)upgrade:(NSInteger)oldVersion {
    //對比數據庫版本
    if (oldVersion >= DBVERSION) {
        return;
    }
    switch (oldVersion) {
        case 0:
            //執行相應的升級操作
            break;
        case 1:
            //執行相應的升級操作
            break;
        case 2:
            //執行相應的升級操作
            break;
        default:
            break;
    }
    oldVersion ++;
    // 遞歸判斷是否需要升級
    [self upgrade:oldVersion];
}

加班通宵之后的又一個晚上我寫下了上面的測試程序,為了美好明天!拼了!能夠時刻保持着一種學習者的心態,也希望我的技能提升的同時也能幫助一部分博友!

FMDB的使用就介紹到此~

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM