zoukankan      html  css  js  c++  java
  • IOS数据存储之Sqlite数据库

    前言:

      之前学习了数据存储的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 <Foundation/Foundation.h>
    
    @interface DBManager : NSObject<NSCopying>
    
    //创建数据库管理者单例
    +(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 <sqlite3.h>
    
    #define DBNAME @"myDb" //数据库名字
    #define TBNAME @"persons" //表名
    #define DBVERSION 1      //数据库版本
    #define DBVERSIONKEY @"dbversion_key" //存储数据库版本key
    
    static 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 <sqlite3.h>
    
    #define DBNAME @"myDb" //数据库名字
    #define TBNAME @"persons" //表名
    #define DBVERSION 1      //数据库版本
    #define DBVERSIONKEY @"dbversion_key" //存储数据库版本key
    
    static 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<testMaxCount;i++){
                   NSString *string = [[NSString alloc] initWithFormat:@"%d",i];
                  [testArray addObject:string];
              }
              
              //未开启事务插入
              CFAbsoluteTime start = CFAbsoluteTimeGetCurrent();
    
              [[DBManager shareManager]insertDataByNomarl:testArray];
              CFAbsoluteTime end=CFAbsoluteTimeGetCurrent();
              NSLog(@"普通插入 time cost: %0.3f", end - start);
              
              //删除数据
              [[DBManager shareManager]deleteData];
              
              //开启事务插入
               start = CFAbsoluteTimeGetCurrent();
              
              [[DBManager shareManager]insertDataByTransaction:testArray];
              
               end=CFAbsoluteTimeGetCurrent();
              NSLog(@"开启事务插入 time cost: %0.3f", end - start);
              
              //删除数据
              [[DBManager shareManager]deleteData];

    测试结果:测试数据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(这个在两年前使用过)。

  • 相关阅读:
    ubuntu 安装 redis desktop manager
    ubuntu 升级内核
    Ubuntu 内核升级,导致无法正常启动
    spring mvc 上传文件,但是接收到文件后发现文件变大,且文件打不开(multipartfile)
    angular5 open modal
    POJ 1426 Find the Multiple(二维DP)
    POJ 3093 Margritas
    POJ 3260 The Fewest Coins
    POJ 1837 Balance(二维DP)
    POJ 1337 A Lazy Worker
  • 原文地址:https://www.cnblogs.com/whoislcj/p/5485959.html
Copyright © 2011-2022 走看看