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(这个在两年前使用过)。

  • 相关阅读:
    Java提倡的命名习惯
    菜鸟涂鸦作品展_No.24
    Java异常处理学习笔记
    菜鸟涂鸦作品展_No.21
    菜鸟涂鸦作品展_No.20
    菜鸟涂鸦作品展_No.23
    对我影响最大的老师
    自我介绍0.0
    xml的标记、元素和属性
    C#(.net)中的DllImport
  • 原文地址:https://www.cnblogs.com/whoislcj/p/5485959.html
Copyright © 2011-2022 走看看