博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
IOS数据存储之Sqlite数据库
阅读量:6367 次
发布时间:2019-06-23

本文共 16139 字,大约阅读时间需要 53 分钟。

前言:

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

  我们在项目开发中需要引入libsqlite3.dylib,那么sqllite有哪些具体方法呢?

sqlite3  *db, 数据库句柄,跟文件句柄FILE很类似sqlite3_stmt      *stmt, 这个相当于ODBC的Command对象,用于保存编译好的SQL语句sqlite3_open(),   打开数据库,没有数据库时创建。sqlite3_exec(),   执行非查询的sql语句Sqlite3_step(), 在调用sqlite3_prepare后,使用这个函数在记录集中移动。Sqlite3_close(), 关闭数据库文件还有一系列的函数,用于从记录集字段中获取数据,如sqlite3_column_text(), 取text类型的数据。sqlite3_column_blob(),取blob类型的数据sqlite3_column_int(), 取int类型的数据

 为了系统而且方面的学习sqlite 整理一个sqlite管理类DBManager,实现功能具体涵盖了:数据库的创建,打开,关闭,升级,数据的增删改查,以及事务的开启和开启事务的好处。

 DBManager.h

#import 
@interface DBManager : NSObject
//创建数据库管理者单例+(instancetype)shareManager;//打开数据库-(void)openDb;//关闭数据库-(void)closeDb;//执行sql语句-(void)execSql:(NSString *)sql;//创建数据库表-(void)creatTable;//删除表结构-(void)dropTable;//插入数据-(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
View Code

 

 DBManager.m

#import "DBManager.h"#import 
#define DBNAME @"myDb" //数据库名字#define TBNAME @"persons" //表名#define DBVERSION 1 //数据库版本#define DBVERSIONKEY @"dbversion_key" //存储数据库版本keystatic DBManager *instance=nil;@implementation DBManager{ //创建数据库实例 sqlite3 *db;}-(instancetype)init{ self=[super init]; if (self) { [self creatTable]; [self upgrade]; } return self;}//创建数据库管理者单例+(instancetype)shareManager{ if(instance==nil){ @synchronized(self){ if(instance==nil){ instance =[[[self class]alloc]init]; } } } return instance;}-(id)copyWithZone:(NSZone *)zone{ return instance;}+(id)allocWithZone:(struct _NSZone *)zone{ if(instance==nil){ instance =[super allocWithZone:zone]; } return instance;}//打开数据库-(void)openDb{ NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *documents = [paths objectAtIndex:0]; NSString *database_path = [documents stringByAppendingPathComponent:DBNAME]; if (sqlite3_open([database_path UTF8String], &db) == SQLITE_OK) { NSLog(@"数据库打开成功"); }else{ [self closeDb]; NSLog(@"数据库打开失败"); } }//关闭数据库-(void)closeDb{ sqlite3_close(db);}//检查数据库是否需要升级- (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];}//执行sql语句-(void)execSql:(NSString *)sql{ [self openDb]; char *err; if (sqlite3_exec(db, [sql UTF8String], NULL, NULL, &err) == SQLITE_OK) { NSLog(@"数据库操作数据成功!"); }else{ sqlite3_free(err); NSLog(@"数据库操作数据失败!"); } sqlite3_close(db);}//创建数据库表-(void)creatTable{ NSString *creatTableSql=[NSString stringWithFormat:@"create table if not exists %@(person_id integer primary key,name text)",TBNAME]; [self execSql:creatTableSql];}//删除数据库表-(void)dropTable{ NSString *dropTableSql=[NSString stringWithFormat:@"drop table %@",TBNAME]; [self execSql:dropTableSql];}//插入数据-(void)insertData:(NSString*)tempName{ NSString *insertSql = [NSString stringWithFormat:@"insert into %@ (name) values ('%@')",TBNAME,tempName]; [self execSql:insertSql];}//插入数据未开启事务-(void)insertDataByNomarl:(NSArray*)tempNames{ [self openDb]; for(NSString *name in tempNames){ NSString *sql = [NSString stringWithFormat:@"insert into %@ (name) values ('%@')",TBNAME,name]; char *err; if (sqlite3_exec(db, [sql UTF8String], NULL, NULL, &err) == SQLITE_OK) { NSLog(@"数据库操作数据成功!"); }else{ sqlite3_free(err); NSLog(@"数据库操作数据失败!"); } } [self closeDb];}//插入数据开启事务-(void)insertDataByTransaction:(NSArray*)tempNames{ @try{ char *errorMsg; [self openDb]; if (sqlite3_exec(db, "BEGIN", NULL, NULL, &errorMsg)==SQLITE_OK) { NSLog(@"启动事务成功"); sqlite3_free(errorMsg); //执行真正的操作 for(NSString *name in tempNames){ NSString *sql = [NSString stringWithFormat:@"insert into %@ (name) values ('%@')",TBNAME,name]; char *err; if (sqlite3_exec(db, [sql UTF8String], NULL, NULL, &err) == SQLITE_OK) { NSLog(@"数据库操作数据成功!"); }else{ sqlite3_free(err); NSLog(@"数据库操作数据失败!"); } } if (sqlite3_exec(db, "COMMIT", NULL, NULL, &errorMsg)==SQLITE_OK) { NSLog(@"提交事务成功"); } sqlite3_free(errorMsg); }else{ sqlite3_free(errorMsg); } } @catch(NSException *e){ char *errorMsg; if (sqlite3_exec(db, "ROLLBACK", NULL, NULL, &errorMsg)==SQLITE_OK) { NSLog(@"回滚事务成功"); } sqlite3_free(errorMsg); } [self closeDb];}//删除数据-(void)deleteData:(NSString*)tempName{ NSString *deleteSql=[NSString stringWithFormat:@"delete from %@ where name = '%@'",TBNAME,tempName]; [self execSql:deleteSql]; }//删除数据-(void)deleteData{ NSString *deleteSql=[NSString stringWithFormat:@"delete from %@ ",TBNAME]; [self execSql:deleteSql];}//修改数据-(void)updateData:(NSString*)tempName{ NSString *updateSql=[NSString stringWithFormat:@"update %@ set name ='test' where name = '%@'",TBNAME,tempName]; [self execSql:updateSql]; }//查询数据-(void)queryData{ [self openDb]; NSString *querySql =[NSString stringWithFormat:@"select * from %@",TBNAME]; sqlite3_stmt *stmt; if (sqlite3_prepare_v2(db, [querySql UTF8String], -1, &stmt, nil) == SQLITE_OK) { while (sqlite3_step(stmt)==SQLITE_ROW) { char *name = (char *)sqlite3_column_text(stmt, 1); NSString *nameString = [[NSString alloc] initWithUTF8String:name]; NSLog(@"nameString---->%@",nameString); } sqlite3_finalize(stmt); } [self closeDb]; }@end
View Code

 

具体使用方法:

#import "DBManager.h"#import 
#define DBNAME @"myDb" //数据库名字#define TBNAME @"persons" //表名#define DBVERSION 1 //数据库版本#define DBVERSIONKEY @"dbversion_key" //存储数据库版本keystatic DBManager *shareManager=nil;@implementation DBManager{ //创建数据库实例 sqlite3 *db;}-(instancetype)init{ self=[super init]; if (self) { [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)openDb{ NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *documents = [paths objectAtIndex:0]; NSString *database_path = [documents stringByAppendingPathComponent:DBNAME]; if (sqlite3_open([database_path UTF8String], &db) == SQLITE_OK) { NSLog(@"数据库打开成功"); }else{ [self closeDb]; NSLog(@"数据库打开失败"); } }//关闭数据库-(void)closeDb{ sqlite3_close(db);}//删除数据库-(void)dropDb{ }//检查数据库是否需要升级- (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];}//执行sql语句-(void)execSql:(NSString *)sql{ [self openDb]; char *err; if (sqlite3_exec(db, [sql UTF8String], NULL, NULL, &err) == SQLITE_OK) { NSLog(@"数据库操作数据成功!"); }else{ sqlite3_free(err); NSLog(@"数据库操作数据失败!"); } sqlite3_close(db);}//创建数据库表-(void)creatTable{ NSString *creatTableSql=[NSString stringWithFormat:@"create table if not exists %@(person_id integer primary key,name text)",TBNAME]; [self execSql:creatTableSql];}//删除数据库表-(void)dropTable{ NSString *dropTableSql=[NSString stringWithFormat:@"drop table %@",TBNAME]; [self execSql:dropTableSql];}//插入数据-(void)insertData:(NSString*)tempName{ NSString *insertSql = [NSString stringWithFormat:@"insert into %@ (name) values ('%@')",TBNAME,tempName]; [self execSql:insertSql];}//插入数据未开启事务-(void)insertDataByNomarl:(NSArray*)tempNames{ [self openDb]; for(NSString *name in tempNames){ NSString *sql = [NSString stringWithFormat:@"insert into %@ (name) values ('%@')",TBNAME,name]; char *err; if (sqlite3_exec(db, [sql UTF8String], NULL, NULL, &err) == SQLITE_OK) { //NSLog(@"数据库操作数据成功!"); }else{ sqlite3_free(err); //NSLog(@"数据库操作数据失败!"); } } [self closeDb];}//插入数据开启事务-(void)insertDataByTransaction:(NSArray*)tempNames{ @try{ char *errorMsg; [self openDb]; if (sqlite3_exec(db, "BEGIN", NULL, NULL, &errorMsg)==SQLITE_OK) { NSLog(@"启动事务成功"); sqlite3_free(errorMsg); //执行真正的操作 for(NSString *name in tempNames){ NSString *sql = [NSString stringWithFormat:@"insert into %@ (name) values ('%@')",TBNAME,name]; char *err; if (sqlite3_exec(db, [sql UTF8String], NULL, NULL, &err) == SQLITE_OK) { //NSLog(@"数据库操作数据成功!"); }else{ sqlite3_free(err); // NSLog(@"数据库操作数据失败!"); } } if (sqlite3_exec(db, "COMMIT", NULL, NULL, &errorMsg)==SQLITE_OK) { NSLog(@"提交事务成功"); } sqlite3_free(errorMsg); }else{ sqlite3_free(errorMsg); } } @catch(NSException *e){ char *errorMsg; if (sqlite3_exec(db, "ROLLBACK", NULL, NULL, &errorMsg)==SQLITE_OK) { NSLog(@"回滚事务成功"); } sqlite3_free(errorMsg); } [self closeDb];}//删除数据-(void)deleteData:(NSString*)tempName{ NSString *deleteSql=[NSString stringWithFormat:@"delete from %@ where name = '%@'",TBNAME,tempName]; [self execSql:deleteSql]; }//删除数据-(void)deleteData{ NSString *deleteSql=[NSString stringWithFormat:@"delete from %@ ",TBNAME]; [self execSql:deleteSql];}//修改数据-(void)updateData:(NSString*)tempName{ NSString *updateSql=[NSString stringWithFormat:@"update %@ set name ='test' where name = '%@'",TBNAME,tempName]; [self execSql:updateSql]; }//查询数据-(void)queryData{ [self openDb]; NSString *querySql =[NSString stringWithFormat:@"select * from %@",TBNAME]; sqlite3_stmt *stmt; if (sqlite3_prepare_v2(db, [querySql UTF8String], -1, &stmt, nil) == SQLITE_OK) { while (sqlite3_step(stmt)==SQLITE_ROW) { char *name = (char *)sqlite3_column_text(stmt, 1); NSString *nameString = [[NSString alloc] initWithUTF8String:name]; NSLog(@"nameString---->%@",nameString); } sqlite3_finalize(stmt); } [self closeDb]; }@end
View Code

 

重点来了,曾经做个IM即时通讯方面,聊天信息相对来说还是比较庞大一点,动不动就是成千上万条聊天信息,有时候执行一个消息已读状态的更新都要耗时很长,那时候偶还没有学习IOS开发,在Android平台上我已经领略过开启事务对效率的提升所带来的喜悦了,那么ios上面是怎么开启事务的呢?效率怎么样呢?让我们一探究竟:

开启事务:

//插入数据开启事务-(void)insertDataByTransaction:(NSArray*)tempNames{    @try{        char *errorMsg;        [self openDb];        if (sqlite3_exec(db, "BEGIN", NULL, NULL, &errorMsg)==SQLITE_OK) {                        NSLog(@"启动事务成功");                        sqlite3_free(errorMsg);                        //执行真正的操作            for(NSString *name in tempNames){                NSString *sql = [NSString stringWithFormat:@"insert into %@ (name) values ('%@')",TBNAME,name];                char *err;                if (sqlite3_exec(db, [sql UTF8String], NULL, NULL, &err) == SQLITE_OK) {                    NSLog(@"数据库操作数据成功!");                }else{                    sqlite3_free(err);                    NSLog(@"数据库操作数据失败!");                }            }                        if (sqlite3_exec(db, "COMMIT", NULL, NULL, &errorMsg)==SQLITE_OK) {                                NSLog(@"提交事务成功");            }                        sqlite3_free(errorMsg);                    }else{            sqlite3_free(errorMsg);        }    }        @catch(NSException *e){                char *errorMsg;        if (sqlite3_exec(db, "ROLLBACK", NULL, NULL, &errorMsg)==SQLITE_OK) {            NSLog(@"回滚事务成功");        }        sqlite3_free(errorMsg);    }    [self closeDb];}

同时准备一个未开启事务的:

//插入数据未开启事务-(void)insertDataByNomarl:(NSArray*)tempNames{     [self openDb];      for(NSString *name in tempNames){        NSString *sql = [NSString stringWithFormat:@"insert into %@ (name) values ('%@')",TBNAME,name];        char *err;        if (sqlite3_exec(db, [sql UTF8String], NULL, NULL, &err) == SQLITE_OK) {            NSLog(@"数据库操作数据成功!");        }else{            sqlite3_free(err);            NSLog(@"数据库操作数据失败!");        }    }    [self closeDb];}

测试程序:

//测试事务          NSMutableArray *testArray =[[NSMutableArray alloc]init];          int testMaxCount =10000;          for(int i=0;i

测试结果:测试数据10000条 单位(秒)

   开启事务耗时:0.049

未开启事务耗时:5.614

看到上面的执行结果 是不是惊呆了。

 关于数据库升级:由于项目业务发展,数据库有可能要考虑到升级,比如数据库新增表或者已有表结构变化,这时候我们就要考虑到数据升级来做版本兼容:

什么时候检查:

-(instancetype)init{    self=[super init];    if (self) {        [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];}

至此原生的Sqlite基础使用就告一段落了,至于高级使用一般情况涉及到的多数是sql语句的使用,sql语句不善长的小伙伴可以去熟悉一下sql数据!这时就在想了IOS有没有像Android一样的第三方数据库框架呢?也让我等sql小白缓解一下压力?特意查询了一下,以下仅供参考:Sqlitepersistentobjects ,FMDB(这个在两年前使用过)。

 

转载地址:http://ggema.baihongyu.com/

你可能感兴趣的文章
monkeyrunner运行Python脚本来检查apk渠道和验证是否可以调用微信
查看>>
github获得SSH Key解决Permission denied (publickey)问题
查看>>
用java代码编写Oracle存储过程
查看>>
APACHE转发
查看>>
android-market-api
查看>>
解決 yum update錯誤:[Errno -1] Metadata file does not match checksum
查看>>
ASP.NET(C#)Excel导入Dataset的出现数据值丢失问题
查看>>
我的友情链接
查看>>
『Data Science』R语言学习笔记,获取数据
查看>>
rails中n秒页面自动跳转
查看>>
我的友情链接
查看>>
忘记root用户密码怎么办?
查看>>
esxi定时任务
查看>>
Scaffold-DbContext
查看>>
关于VMware Workstation主机列表问题求教
查看>>
配置管理小报101021:给ubuntu加监控
查看>>
qml文字滚动效果的封装,实现方式运用的qml中提供的动画效果,另一种实现方式也可以使用定时器修改控件的坐标来实现...
查看>>
标准C++实现任务队列
查看>>
jdbc url
查看>>
刷leetcode第704题-二分查找
查看>>