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 源码托管地址:
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 == ) {
return;
} //升级
[self upgrade:oldVersionNum]; // 保存新的版本号到库中 -这里大家可以使用NSUserDefault存储
[[NSUserDefaults standardUserDefaults]setInteger:DBVERSION forKey:DBVERSIONKEY];
} //根据不同版本执行不同的升级逻辑
- (void)upgrade:(NSInteger)oldVersion {
//对比数据库版本
if (oldVersion >= DBVERSION) {
return;
}
switch (oldVersion) {
case :
//执行相应的升级操作
break;
case :
//执行相应的升级操作
break;
case :
//执行相应的升级操作
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=;i<;i++){
NSString *string = [[NSString alloc] initWithFormat:@"%d",i];
[[FMDBManager shareManager]insertData:string];
}
//然后查询一下
[[FMDBManager shareManager]queryData];
//然后删除一条数据
[[FMDBManager shareManager]deleteData:@""];
//更新数据
[[FMDBManager shareManager]updateData:@""];
//然后查询一下
[[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 =;
for(int i=;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 == ) {
return;
} //升级
[self upgrade:oldVersionNum]; // 保存新的版本号到库中 -这里大家可以使用NSUserDefault存储
[[NSUserDefaults standardUserDefaults]setInteger:DBVERSION forKey:DBVERSIONKEY];
} //根据不同版本执行不同的升级逻辑
- (void)upgrade:(NSInteger)oldVersion {
//对比数据库版本
if (oldVersion >= DBVERSION) {
return;
}
switch (oldVersion) {
case :
//执行相应的升级操作
break;
case :
//执行相应的升级操作
break;
case :
//执行相应的升级操作
break;
default:
break;
}
oldVersion ++;
// 递归判断是否需要升级
[self upgrade:oldVersion];
}
加班通宵之后的又一个晚上我写下了上面的测试程序,为了美好明天!拼了!能够时刻保持着一种学习者的心态,也希望我的技能提升的同时也能帮助一部分博友!
FMDB的使用就介绍到此~
IOS数据存储之FMDB数据库的更多相关文章
- IOS数据存储之Sqlite数据库
前言: 之前学习了数据存储的NSUserDefaults,归档和解档,沙盒文件存储,但是对于数据量比较大,需要频繁查询,删除,更新等操作的时候无论从效率上还是性能上,上述三种明显不能满足我们的日常开发 ...
- IOS 数据存储之 FMDB 详解
FMDB是用于进行数据存储的第三方的框架,它与SQLite与Core Data相比较,存在很多优势. FMDB是面向对象的,它以OC的方式封装了SQLite的C语言API,使用起来更加的方便,不需要过 ...
- ios数据存储方式FMDB
本文转载至 http://blog.csdn.net/chen505358119/article/details/9289489 分类: ios2013-07-10 14:05 2471人阅读 评论( ...
- iOS数据存储之对象归档
iOS数据存储之对象归档 对象归档 对象归档是iOS中数据持久化的一种方式. 归档是指另一种形式的序列化,但它是任何对象都可以实现的更常规的类型.使用对模型对象进行归档的技术可以轻松将复杂的对象写入文 ...
- iOS数据存储之属性列表理解
iOS数据存储之属性列表理解 数据存储简介 数据存储,即数据持久化,是指以何种方式保存应用程序的数据. 我的理解是,开发了一款应用之后,应用在内存中运行时会产生很多数据,这些数据在程序运行时和程序一起 ...
- 在ASP.NET Core中如何支持每个租户数据存储策略的数据库
在ASP.NET Core中如何支持每个租户数据存储策略的数据库 不定时更新翻译系列,此系列更新毫无时间规律,文笔菜翻译菜求各位看官老爷们轻喷,如觉得我翻译有问题请挪步原博客地址 本博文翻译自: ht ...
- 【18】如何把数据存储到MongoDB数据库
如何把数据存储到MongoDB数据库 时间:2018.10.31 edit by :北鼻 一.mongoDB环境安装 需要使用mongoDB数据库的话需要安装环境, ...
- 猫眼电影爬取(一):requests+正则,并将数据存储到mysql数据库
前面讲了如何通过pymysql操作数据库,这次写一个爬虫来提取信息,并将数据存储到mysql数据库 1.爬取目标 爬取猫眼电影TOP100榜单 要提取的信息包括:电影排名.电影名称.上映时间.分数 2 ...
- iOS数据存储类型 及 堆(heap)和栈(stack)
iOS数据存储类型 及 堆(heap)和栈(stack) 一般认为在c中分为这几个存储区: 1栈 -- 由编译器自动分配释放. 2堆 -- 一般由程序员分配释放,若程序员不释放,程序结束时可能由O ...
随机推荐
- 20161023 NOIP 模拟赛 T1 解题报告
Task 1.纸盒子 (box.pas/box.c/box.cpp) [题目描述] Mcx是一个有轻度洁癖的小朋友.有一天,当他沉溺于数学卷子难以自拔的时候,恍惚间想起在自己当初学习概率的时候准备的一 ...
- 51nod算法马拉松15
智力彻底没有了...看来再也拿不到奖金了QAQ... A B君的游戏 因为数据是9B1L,所以我们可以hash试一下数据... #include<cstdio> #include<c ...
- 李洪强iOS经典面试题154- 通知与推送
李洪强iOS经典面试题154- 通知与推送 通知与推送 本地通知和远程推送通知对基本概念和用法? image 本地通知和远程推送通知都可以向不在前台运行的应用发送消息,这种消息既可能是即将发生的事 ...
- String对象方法扩展
/** *字符串-格式化 */ String.prototype.format = function(){ var args = arguments;//获取函数传递参数数组,以便在replace回调 ...
- Mysql 表分区
是否支持分区:mysql> show variables like '%partition%';+-----------------------+-------+| Variable_name ...
- 初识CoreData与详解
Core Data数据持久化是对SQLite的一个升级,它是iOS集成的,在说Core Data之前,我们先说说在CoreData中使用的几个类. (1)NSManagedObjectModel(被管 ...
- PDF转JPG
问题:对于上次解决PB调用不起PDF插件的问题,所以将PDF转换为HTML.但是转换完之后发现一个问题,pdf2htmlEX插件转换出的html只支持IE9以上,IE9以下的都会各式错乱, 所以只能换 ...
- Javascript外部对象
Window 浏览器: - location:地址 - history:历史 - Document:文档 - screen:窗口 - navigator:帮助 > 1.外部对象就是浏览器提供的A ...
- 在C#中使用消息队列RabbitMQ
1.什么是RabbitMQ.详见 http://www.rabbitmq.com/. 作用就是提高系统的并发性,将一些不需要及时响应客户端且占用较多资源的操作,放入队列,再由另外一个线程,去异步处理这 ...
- 微信支付:JSAPI支付一直提示URL未注册
今天意外碰上了这个问题,想想微信的坑真多…… 解决办法: 首先要看微信公众号里的 支付授权目录 是否已正确填写,还要验证 url大小写 必须相同 其次查看一下自己请求的地址是否与上面填写的是否一样!u ...