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 ...
随机推荐
- bzoj1008快速面
快速面裸题(显然的m^n-m*(m-1)^(n-1)) 然后,,,就没有然后了 #include <cstdio> #define wzf2000 100003 long long n,m ...
- PHP 小数点保留两位【转】
最近在做统计这一块内容,接触关于数字的数据比较多, 用到了三个函数来是 数字保留小数后 N 位: 接下来简单的介绍一下三个函数: 1.number_format echo number_format( ...
- css的书写规范+常用
格式化: body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,textarea,p,blo ...
- *HDU 1709 母函数
The Balance Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total ...
- [LeetCode]444. Sequence Reconstruction
Check whether the original sequence org can be uniquely reconstructed from the sequences in seqs. Th ...
- WDM驱动加载方式理解
当PC得知有新设备插入时,总线驱动会创建相应的物理驱动PDO,然后提示有新设备插入,这时候调用相应Driver的AddDevice方法创建功能驱动FDO 下面是一个典型的AddDevice方法 #pr ...
- python字符串的使用
之前在网上看了关于python最基础的一些教程,看着都通俗易懂,但是在写的过程中却感觉还是很生涩.关于字符串的使用还是应该多写多练!如何将“teacher_id = 123 #老师ID”转换成字典或者 ...
- Event Store 2.0发布,带来了安全支持和测试版Projections库
Event Store 2.0版本于上周发布,它带来了安全支持允许锁定Event Store和在事件流上设置访问控制列表.其主要新特性包括: HTTP和TCP之上的身份认证,包括账户管理 测试版Pro ...
- 送干货,实用内联gulp插件——gulp-embed
现在npm上有很多gulp内联工具,用于把脚本和样式内嵌到HTML页面上,之前搞项目我也在这些插件中寻觅许久,但均不满足公司项目的一个需求—— HTML上同时插入了开发(dev版,src文件夹下,比如 ...
- UI控件(UIPickerView)
@implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; _item1 = [[NSArray alloc]i ...