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的使用就介绍到此~

时间: 2024-11-07 03:15:20

IOS数据存储之FMDB数据库的相关文章

IOS 数据存储之 FMDB 详解

FMDB是用于进行数据存储的第三方的框架,它与SQLite与Core Data相比较,存在很多优势. FMDB是面向对象的,它以OC的方式封装了SQLite的C语言API,使用起来更加的方便,不需要过多的关心数据库操作的知识.但是它本身也存在一些问题,比如跨平台,因为它是用oc的语言封装的,所以只能在ios开发的时候使用,如果想实现跨平台的操作,来降低开发的成本和维护的成本,就需要使用比较原始的SQLite. Core Data是ORM的一种体现,使用Core Data需要用到模型数据的转化,虽

IOS数据存储之Sqlite数据库

前言: 之前学习了数据存储的NSUserDefaults,归档和解档,沙盒文件存储,但是对于数据量比较大,需要频繁查询,删除,更新等操作的时候无论从效率上还是性能上,上述三种明显不能满足我们的日常开发需要了.这个时候我们必须借助数据库,做为Android开发的都知道采用的是一种轻量级数据库Sqlite.其实它广泛用于包括浏览器.IOS,Android以及一些便携需求的小型web应用系统.它具备占用资源低,处理速度快等优点.接下来我们具体认识一下. 我们在项目开发中需要引入libsqlite3.d

ios数据存储方式FMDB

本文转载至 http://blog.csdn.net/chen505358119/article/details/9289489 分类: ios2013-07-10 14:05 2471人阅读 评论(0) 收藏 举报 从网上下载FMDB的原码,将其拖入到项目中,然后在Link Binary With Libraries中添加libsqlite3.dylib,就可以用了 实现如下: [plain] view plaincopyprint? #import "ViewController.h&quo

iOS数据存储之SqLite3

iOS中数据存储的方式有很多中,当数据量较大的时候偏好设置,归档和plist就无法满足需求了 这时候就需要用SqLite或者CoreData来存储数据 下面就来介绍一下如何使用SqLite存储数据 要使用Sqlite必须引入libSqlite3.dylib库 要使用首先要有一个handle句柄(handle句柄,在C语言中,通常把用于控制某类东西的叫做句柄,实际上是一个指针.) // 数据库句柄 sqlite3 *_db; SqLite存储数据时也是存在一个文件中的,只不过这个文件格式是定制的,

iOS数据存储之CoreData

iOS中大量数据的储存一个是SqLite,另一个就是CoreData,CoreData允许程序员以面向对象的思维方式的方法去操作面向表的数据库 做过Java开发的对这个应该很熟悉,Java中的Hibernate跟CoreData就很相似 CoreData应该怎样使用呢? 第一步,新建工程后导入CoreData框架 第二部,创建CoreData的数据模型创建步骤如下 然后给你的model起个名字,创建完成后你会看到一个这个文件(相当于数据库文件) 点击这个文件,然后看下图 点击图中1,新建实体(类

IOS数据存储 —— 2 存储方式

IOS数据存储方式 iOS开发常用数据存储方式有:plist.偏好设置 NSUserDefaults.对象归档 NSKeyedArchiver.SQLite3和Core Data 1. plist文件 存储 plist文件通常用于储存用户设置,利用xml属性列表归档NSDictionary.NSArray.NSNumber等类型数据 在使用plist进行数据存储和读取,只适用于系统自带的一些常用类型才能用 注意:plist不能存储自定义对象 2. 偏好设置 NSUserDefaults 偏好设置

iOS数据存储的几种方式

iOS的数据存储是iOS应用开发的重要知识点: 关于这方面知识,网上有很多介绍,但对于代码层次的使用方式并未有系统全面介绍.此文章针对iOS稍熟悉的童鞋,需要对CoreData的原理有一定的了解.目前存储方式大概有以下几种: NSKeyedArchiver  适用简单数据加密 NSUserDefaults  适用配置参数 Write  文件操作,同NSKeyedArchiver SQLite3  操作较复杂,不建议使用. CoreData  取代SQLite3,但要遵循NSManagedObje

iOS数据存储类型 及 堆(heap)和栈(stack)

iOS数据存储类型 及 堆(heap)和栈(stack) 一般认为在c中分为这几个存储区: 1栈 --  由编译器自动分配释放. 2堆 --  一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收. 3全局区(静态存储区)-- 全局变量和静态变量的存储是放在一块区域 ,程序退出后自动释放 .全局区又分为全局初始化区和全局未初始化区.初始化的全局变量和静态变量存放在全局初始化区,未初始化的全局变量和未初始化的静态变量存放在相邻的另一块区域. 4常量区-- 专门放数字/字符常量的地方, 程

iOS数据存储之属性列表理解

iOS数据存储之属性列表理解 数据存储简介 数据存储,即数据持久化,是指以何种方式保存应用程序的数据. 我的理解是,开发了一款应用之后,应用在内存中运行时会产生很多数据,这些数据在程序运行时和程序一起驻留在内存中,一旦程序运行结束从内存中退出后,这些数据也就相应消失了.等到再次运行程序的时候,之前的那些数据又要重新计算.但是对于一些应用,我们需要将程序产生的数据持久的保存起来,使得应用重启之后这些数据不会丢失,这时候就需要用到数据的持久化技术. 在iOS设备上实现数据持久化存储的方式有很多中机制