iOS开发FMDB的简单使用

题记:

时间飞快,转眼又是两个月,这期间又经历了一个小的app,之后接触了一个新的内容IM,当然最近一段时间内,这个也是一直在做的;

其实更新的blog的想法已有好久,一直没确定好内容,正好IM SDK中涉及到conversation和message的本地化处理,就选了这个主题;

实话讲,在写这个项目之前,我对数据库真的不熟,甚至sql语句都记不清了(我真的学过^_^),为此我买了一本O REILY的《SQL学习指南》读了一遍,书很好推荐给大家;

最终,在CoreData和FMDB之间,我选择了使用FMDB进行本地数据库的操作;


客户端的数据持久化方案:

常用的主要是NSUserDefault和File,Keychain的方式用的不多,也算上,但这些都是处理简单的数据存储,比如我对app登录之后的用户信息存储多数就是通过NSUserDefault进行的;

file的方式每次操作都涉及到文件的读取,稍麻烦些;

对于复杂的数据结构,还是要用到数据库,iOS提供的CoreData实际上是database数据存储和model对象之间转换的一种机制;


SQLite:

sqlite是移动端的轻量级关系数据库解决方案,应用范围也很广;数据库的知识范围很广,简单用也简单,用的溜得也有,比如微信,推荐读下《微信iOS SQLite源码优化实践》;

大部分人所熟知的CoreData和FMDB所操作的数据库,内核都是基于sqlite的;原生是支持SQLite的,只不过API并不友好(道听途说,真没亲眼见过……),开源社区中对API进行封装的库FMDB做的很好,也最常用;


FMDB:

FMDB是对iOS相关SQLite的API进行封装的第三方库,使用者只需调用该框架的API就能很方便的进行本地数据库的操作;

FMDB操作数据库并不消耗太多的性能,而且提供了线程不安全的解决方案;

将FMDB copy到我们的项目,我们可以看到他的目录结构:

核心的三个类:

1)FMDatabase:一个FMDatabase对象就代表一个单独的SQLite数据库;

2)FMResultSet:使用FMDB进行查询得到的结果集合;

3)FMDatabaseQueue:用于解决线程不安全的类,避免每个线程创建一个数据库;


使用:

FMDB提供了创建数据库的接口,我们可以使用

+ (instancetype)databaseWithPath:(NSString*)inPath;

进行数据库的创建,inPath是我们指定的一个沙河路径;

创建表的话,直接执行创建表的sql语句即可;

在实际项目的使用中,我的做法是在资源路径下事先创建好db.sqlite数据库,同时表结构也是事先创建好的,在调用创建数据库时,将该数据库从资源路径copy到沙河路径即可;

[fileManager copyItemAtPath:srcPath toPath:verFilePath error:&error]


在操作数据库之前需要先打开数据库open,通常在执行完相关操作时候,需要调用close方法来关闭数据库;


对于数据库的操作,除了查询以外,其他的都可以理解为更新:

- (FMResultSet *)executeQuery:(NSString*)sql, ... 

- (BOOL)executeUpdate:(NSString*)sql, ... 

执行相应的sql语句即可,先看段示例代码:

FMResultSet *rs_message = [fmdb executeQuery:@"SELECT * FROM message WHERE conversation=? AND username=?;",conversationId,[_sharedManager getUserId]];
            while ([rs_message next]){
                
                result = [[HQCMessageModel alloc]init];
                
                result.msgid = [rs_message stringForColumn:@"msgid"];
                result.msgfrom = [rs_message stringForColumn:@"msgfrom"];
                result.msgto = [rs_message stringForColumn:@"msgto"];
                result.msgtype = [NSNumber numberWithInt:[rs_message intForColumn:@"msgtype"]];
                result.msgbody = [rs_message stringForColumn:@"msgbody"];
                result.conversation =  [rs_message stringForColumn:@"conversation"];
                result.msgatt =  [rs_message stringForColumn:@"msgatt"];
                result.atttype = [NSNumber numberWithInt:[rs_message intForColumn:@"atttype"]];
                result.msgtime = [NSNumber numberWithLongLong:(long long)[rs_message intForColumn:@"msgtime"]];
                result.msgdirection = [NSNumber numberWithInt:[rs_message intForColumn:@"msgdirection"]];
                result.isdelivered = [NSNumber numberWithInt:[rs_message intForColumn:@"isdelivered"]];
                result.status = [NSNumber numberWithInt:[rs_message intForColumn:@"status"]];
                result.servertime = [NSNumber numberWithLongLong:(long long)[rs_message intForColumn:@"servertime"]];
                
                result.username = [rs_message stringForColumn:@"username"];
                
            }
            [rs_message close];

这是我从数据库查询message时的一段代码,我们发现,对于rs_message结果集,需要调用next方法,获取相应的查询记录;

结尾的结果集关闭,可以不调用,因为当相关数据库关闭时,该结果集也会自动关闭;

我们的message model记录的字段类型各异,FMDB也为此提供了多个方法来获取不同类型的数据,示例代码中只用了几种,其他的大家可以看下具体的API;


上面的sql语句中,我们按照标准的SQL语句,用?表示了执行语句的参数,这里需要注意:

该参数对应的数据必须为NSObject的子类,对于基本数据类型,需要进行类型装换;


FMDB使用FMDatabaseQueue来保证线程安全,再看一段示例代码:

    FMDatabaseQueue * queue = [FMDatabaseQueue databaseQueueWithPath:filePath];
    [queue inDatabase:^(FMDatabase *fmdb) {
        [fmdb open];
        if (![fmdb tableExists:@"message"]) {
            result = nil;
            [fmdb close];
            return;
        }
        [fmdb setShouldCacheStatements:YES];
        
        FMResultSet *rs_message = [fmdb executeQuery:@"SELECT * FROM message WHERE msgid=? AND username=?;",messageId,[_sharedManager getUserId]];

这只是一段代码片段,但我们可以清楚的知道FMDatabaseQueue的使用:

首先用数据库文件地址来初始化,在将一个闭包(block)传入,在闭包中完成数据库的操作;


事物(Transaction)是并发控制的基本单位,不可分割,简单来说事物是一种机制,用以维护数据库的完整性,大家可以参看一下《数据库事物 ios FMDB》这篇文章;

需要使用数据库事物的一般是更新和删除等操作;

对于FMDB的事务处理,我们可以参见如下示例代码:

BOOL isSuccess = NO;
        [fmdb beginTransaction];
        @try {//事务,有异常则回滚
            BOOL rs;

            rs  = [fmdb executeUpdate:@"UPDATE conversation SET unreadcount=?,timestamp=? WHERE converid=? AND username=?;",
                   conversation.unreadcount,
                   date,
                   conversation.converid,
                   [_sharedManager getUserId]
                   ];
            
            if (rs) {
                result++;
            }else{
                result = 0;
            }
        }
        @catch (NSException *exception) {
            isSuccess = NO;
            [fmdb rollback];
        }
        @finally {
            isSuccess = YES;
            [fmdb commit];
        }

以上这些就是FMDB对数据库的基本操作,在实现时可以统一放到一个DBManager类中,统一提供接口;

FMDB对系统API的封装,使对数据库的操作变得很简单;


总结:

对于客户端的开发,数据库的使用是基本的技能之一,之前的项目都是直接联网请求数据展示的,对于数据本地化并没有要求,但并不代表它不重要,相反当业务要求的数据结构较复杂时,数据库的作用就得以凸显,适当的减少了网络交互,同时也能增加app的流畅性和体验,比如,在实现我们的IM SDK时,消息、会话等数据就一定要进行本地处理;

我们的IM SDK是为解决公司内部诸产品线对即时通信的技术需求而实现的,目前还在开发,在这个过程中,体会到了自己的不足,也学习了很多,有机会慢慢分享给大家;


感谢新一阶段的自己(也是为了提醒自己),使这篇文章成为可能,谢谢!


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值