美文网首页
iOS 本地数据库持久化存储 (包含版本升级和切换账号的问题)

iOS 本地数据库持久化存储 (包含版本升级和切换账号的问题)

作者: yuezishenyou | 来源:发表于2018-11-18 19:41 被阅读0次

    概述

    移动app端数据库的创建,在思想上有两种方式:
    a.创建通用表(id, time, data), time是操作时间,data是存放json数据的字符串(把数据转json字符串存放)。这样的好处是不需要想着数据库升级的问题,但若是有查询类似点赞数量就感觉不太适合了。
    b.创建正常表,有什么字段写什么字段。但这样会涉及到表字段的增加,也就是数据库的升级。

    参考文档:
    a:这是唐巧大神的文章 通用表
    b:这是一位高手的文章 正常表

    正文

    我今天说的是b种数据库。解决了app切换账号数据库的更换和数据库版本升级问题。

    总体思想:

    1.切换账号:例如手机号登录,以手机号来命名数据库,这样解决不同手机登录,来访问不同的数据库。还有种思想是一个数据库,表不同,某个功能表前加前缀来区分,但是我想象感觉有点麻烦,去升级和添加字段的时候。

    2.版本升级:它也有两种思路,一种是直接sql命令添加需要的字段名和类型。另一种是创建新表,把老表数据拷贝到新表里去,然后删去老表,更改新表的名字。

    代码展示

    这是我写的 demo
    欢迎点赞收藏和批评。每个语言都博大精深我们应该谦虚向学。

    IGFMDB.h 文件

    #import <Foundation/Foundation.h>
    #import "IGParameters.h"
    
    NS_ASSUME_NONNULL_BEGIN
    
    
    
    // 数据库支持的类型(如果不满足条件以下条件,那么在后续会增加)
    typedef NS_ENUM(NSUInteger, IGFMDBValueType) {
        IGFMDBValueTypeString,     // 字符串
        IGFMDBValueTypeInteger,    // 整型,长整型,bool值 都是integer
        IGFMDBValueTypeFloat,      // 浮点型,double
        IGFMDBValueTypeData,       // 二进制数据
    };
    
    // 数学运算的类型
    typedef NS_ENUM(NSUInteger, IGFMDBMathType) {
        IGFMDBMathTypeSum,         // 总和
        IGFMDBMathTypeAvg,         // 平均值
        IGFMDBMathTypeMax,         // 最大值
        IGFMDBMathTypeMin,         // 最小值
    };
    
    
    
    
    
    
    
    @interface IGFMDB : NSObject
    
    #pragma mark - 初始化
    
    + (instancetype) shareDatabase; //会默认创建一个db
    
    - (void) createDBWithName:(NSString *)dbName;
    
    - (void) createDBWithName:(NSString *)dbName path:(NSString *)dbPath;
    
    
    - (void)upgradeDatabase:(NSString *)dbName;
    
    
    
    
    
    
    #pragma mark - 创建表
    
    /**
     *  根据传入的Model去创建表(推荐使用此方法)
     *  @param modelClass
        model的属性名称作为表的key值, 属性的value的类型也就是表里面的value的类型,如value可以是NSString,integer,float,bool等
     *  @param excludedProperties  被排除掉属性,这些属性被排除掉之后则不会存在数据库当中
     *  @param tableName           表名,不可以为nil
     *  @return 是否创建成功
     */
    - (BOOL)createTableWithModelClass:(Class _Nonnull)modelClass
                   excludedProperties:(NSArray<NSString *> * _Nullable)excludedProperties
                            tableName:(NSString * _Nonnull)tableName;
    
    
    
    
    
    
    
    #pragma mark - 插入数据
    
    /**
     *  插入一条数据(推荐使用)
     *  @param model        需要插入Model
     *  @param tableName    表名,不可以为nil
     *  @return             是否插入成功
     */
    - (BOOL)insertWithModel:(id _Nonnull)model tableName:(NSString * _Nonnull)tableName;
    
    
    /**
     *  插入多条数据
     *  @param models       需要插入的存放Model的数组。其中必须要保证数组内的Model都是同一类型的Model
     *  @param tableName    表名,不可以为nil
     *  在连续插入多条数据的时候,很有可能会出现插入不成功的情况,如果想要联调,请将shouldOpenDebugLog设为YES
     */
    - (void)insertWithModels:(NSArray *)models tableName:(NSString * _Nonnull)tableName;
    
    
    
    
    
    
    #pragma mark - 删除数据
    
    /**
     *  根据参数删除表中的数据
     *  @param tableName     表的名字
     *  @param parameters    参数,IGParameters决定了sql语句"where"后面的参数。
     *  @return 是否删除成功
     */
    - (BOOL)deleteFromTable:(NSString * _Nonnull)tableName whereParameters:(IGParameters *)parameters;
    
    /**
     *  删除所有数据
     *  @param tableName    同上
     *  @return             同上
     */
    - (BOOL)deleteAllDataFromTable:(NSString * _Nonnull)tableName;
    
    
    
    
    
    #pragma mark - 更改数据
    
    /**
     *  根据参数删除表中的数据
     *  @param tableName    表的名字,不可以为nil
     *  @param dictionary   要更新的key-value.在我经验来看,更改典里部分数据
     *  @param parameters   参数,IGParameters决定了sql语句"where"后面的参数
     */
    - (BOOL)updateTable:(NSString * _Nonnull)tableName dictionary:(NSDictionary * _Nonnull)dictionary whereParameters:(IGParameters *)parameters;
    
    
    
    
    
    #pragma mark - 查询数据
    
    /**
     *  根据参数删除表中的数据
     *  @param tableName    表的名字,不可以为nil
     *  @param modelClass   modelClass里属性的都当key拿值
     *  @param parameters   参数,IGParameters决定了sql语句"where"后面的参数
     *  @return             返回所有符合条件的数据
     */
    - (NSArray *)queryFromTable:(NSString * _Nonnull)tableName model:(Class _Nonnull)modelClass whereParameters:(IGParameters *)parameters;
    
    
    
    
    #pragma mark - 除去增删改查之外常用的功能
    
    
    /**
     *  表是否存在
     *  @param tableName    表的名字
     *  @return             表是否存在
     */
    - (BOOL)existTable:(NSString * _Nonnull)tableName;
    
    /**
     *  为一个表增加字段
     *  @param tableName    表的名字
     *  @param column       要增加的字段
     *  @param type         增加的字段类型
     *  @return             是否添加成功
     */
    - (BOOL)alterTable:(NSString * _Nonnull)tableName column:(NSString * _Nonnull)column type:(IGFMDBValueType)type;
    
    /**
     *  删除一张表
     *  @param tableName    表的名字
     *  @return             是否删除成功
     */
    - (BOOL)dropTable:(NSString * _Nonnull)tableName;
    
    /**
     *  获取某一个表中所有的字段名
     *  @param tableName    表的名字
     *  @return             所有字段名
     */
    - (NSArray<NSString *> *)getAllColumnsFromTable:(NSString * _Nonnull)tableName;
    
    /**
     *  获取表中有多少条数据
     *  @param tableName    表的名字
     *  @param parameters   参数,IGParameters决定了sql语句"where"后面的参数
     *  @return             数据的个数
     */
    - (long long int)numberOfItemsFromTable:(NSString * _Nonnull)tableName whereParameters:(IGParameters * _Nullable)parameters;
    
    /**
     *  数学相关操作
     *  @param type         数学运算的type
     *  @param tableName    表的名字
     *  @param parameters   参数,IGParameters决定了sql语句"where"后面的参数
     *  @return             计算的值
     */
    - (double)numberWithMathType:(IGFMDBMathType)type table:(NSString * _Nonnull)tableName column:(NSString * _Nonnull)column whereParameters:(IGParameters * _Nullable)parameters;
    
    
    
    
    
    @end
    
    NS_ASSUME_NONNULL_END
    
    

    IGFMDB.m 文件

    #import "IGFMDB.h"
    #import <FMDB/FMDB.h>
    #import <objc/runtime.h>
    
    #ifdef DEBUG
    #define debugLog(...)    NSLog(__VA_ARGS__)
    #define debugMethod()    NSLog(@"%s", __func__)
    #define debugError()     NSLog(@"Error at %s Line:%d", __func__, __LINE__)
    #else
    #define debugLog(...)
    #define debugMethod()
    #define debugError()
    #endif
    
    #define PATH_OF_DOCUMENT    [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0]
    
    #define kDatabaseVersionKey     @"YH_DBVersion" //数据库版本
    
    
    static NSString * const DEFAULT_DB_NAME = @"database.sqlite";
    static NSString * const ig_primary_key  = @"primaryId";     // 主键
    static NSString * const ig_sql_text     = @"text";          // 字符串
    static NSString * const ig_sql_real     = @"real";          // 浮点型
    static NSString * const ig_sql_blob     = @"blob";          // 二进制
    static NSString * const ig_sql_integer  = @"integer";       // 整型
    
    
    
    @interface IGFMDB ()
    
    @property (strong, nonatomic) FMDatabaseQueue * dbQueue;
    
    @property (nonatomic, assign) int currentDBVersion;  //当要升级就给这个值赋值
    
    @end
    
    @implementation IGFMDB
    {
        // 保证创建sql语句时的线程安全
        dispatch_semaphore_t _sqlLock;
    }
    
    
    - (void)close {
        [_dbQueue close];
        _dbQueue = nil;
    }
    
    // 校验表名
    - (BOOL)checkTableName:(NSString *)tableName {
        if (tableName == nil || tableName.length == 0 || [tableName rangeOfString:@" "].location != NSNotFound) {
            debugLog(@"ERROR, table name: %@ format error.", tableName);
            return NO;
        }
        return YES;
    }
    
    - (BOOL)isStringVaild:(id)object  {
         return [object isKindOfClass:[NSString class]] && ((NSString*)object).length > 0;
    }
    
    
    
    
    #pragma mark -单利,创建默认DB
    
    + (instancetype) shareDatabase {
        static IGFMDB *_instance = nil;
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            if (_instance == nil) {
                _instance = [[IGFMDB alloc] init];
            }
        });
        return _instance;
    }
    
    - (instancetype) init {
        if (self = [super init]) {
            _currentDBVersion = 0;
            _sqlLock = dispatch_semaphore_create(1);
            [self createDBWithName:DEFAULT_DB_NAME];
        }
        return self;
    }
    
    
    
    
    #pragma mark -创建DB方法
    
    - (void) createDBWithName:(NSString *)dbName {
        
        [self createDBWithName:dbName path:PATH_OF_DOCUMENT];
    }
    
    - (void) createDBWithName:(NSString *)dbName path:(NSString *)dbPath {
        
        NSString *path = [dbPath stringByAppendingPathComponent:dbName];
        
        debugLog(@"-----path: %@ \n-----",path);
        
        if (_dbQueue) {
            [self close];
        }
        _dbQueue = [FMDatabaseQueue databaseQueueWithPath:path];
        
        
    
        
    }
    
    #pragma mark -数据库升级模块
    //这样写还是很好的, 不可以参数暴露在外面
    - (void)upgradeDatabase:(NSString *)dbName
    {
        int dbVersion = [self getDBVersion:dbName];
        
        if (_currentDBVersion > dbVersion) {
            //debugLog(@"-----升级操作: 为student表 增加个sound------");
            if ([self existTable:@"student"]) {
                [self alterTable:@"student" column:@"sound" type:IGFMDBValueTypeData];
                [self setDBVersion:_currentDBVersion dbName:dbName];
                //debugLog(@"-----升级操作成功-----");
            } 
        }
        
        
    }
    
    
    
    
    - (int)getDBVersion:(NSString *)dbName {
        return (int)[[NSUserDefaults standardUserDefaults] integerForKey:kDatabaseVersionKey];
    }
    
    - (void)setDBVersion:(int)version dbName:(NSString *)dbName {
        [[NSUserDefaults standardUserDefaults] setInteger:version forKey:kDatabaseVersionKey];
        [[NSUserDefaults standardUserDefaults] synchronize];
    }
    
    
    
    
    
    
    #pragma mark -创建表
    
    - (BOOL)createTableWithModelClass:(Class _Nonnull)modelClass excludedProperties:(NSArray<NSString *> * _Nullable)excludedProperties tableName:(NSString * _Nonnull)tableName {
        
        if (![self checkTableName:tableName]) {
            return NO;
        }
    
        IGLock(_sqlLock);
        NSString *pkID = ig_primary_key;
        NSMutableString *sqliteString = [NSMutableString  stringWithFormat:@"create table if not exists %@ (%@ integer primary key",tableName, pkID];
        IGUnLock(_sqlLock);
    
        NSDictionary *properties = [self getPropertiesWithModel:modelClass]; //获取model的所有属性以及类型
        for (NSString *key in properties) {
            if ([excludedProperties containsObject:key]) {
                continue;
            }
            [sqliteString appendFormat:@", %@ %@", key, properties[key]];
        }
        [sqliteString appendString:@")"];
    
        __block BOOL res ;
        [_dbQueue inDatabase:^(FMDatabase * _Nonnull db) {
            res = [db executeUpdate:sqliteString];
        }];
    
        return res;
    }
    
    #pragma mark - 插入数据
    
    - (BOOL)insertWithModel:(id _Nonnull)model tableName:(NSString * _Nonnull)tableName {
        
        if (![self checkTableName:tableName]) {
            return NO;
        }
        
        if (model)
        {
            IGLock(_sqlLock);
            NSMutableString *sqliteString = [NSMutableString stringWithFormat:@"insert into %@ (", tableName];
            NSArray *columns =  [self getAllColumnsFromTable:tableName dbQueue:self.dbQueue isIncludingPrimaryKey:NO];
            NSMutableArray *values = [NSMutableArray array];
            for (int index = 0; index < columns.count; index++) {
                [values addObject:@"?"];
            }
            [sqliteString appendFormat:@"%@) values (%@)", [columns componentsJoinedByString:@","], [values componentsJoinedByString:@","]];
            IGUnLock(_sqlLock);
            
            
            __block BOOL isSuccess ;
            NSArray *arguments = [self getValuesFromModel:model columns:columns];
            [_dbQueue inDatabase:^(FMDatabase * _Nonnull db) {
                isSuccess = [db executeUpdate:sqliteString withArgumentsInArray:arguments];
            }];
            if (!isSuccess) {
                debugLog(@"----插入失败----");
            }
            else {
                debugLog(@"----插入成功----");
            }
            return  isSuccess;
            
        }
        else {
            
            return NO;
        }
        
        
    }
    
    
    - (void)insertWithModels:(NSArray *)models tableName:(NSString * _Nonnull)tableName {
        
        if (![self checkTableName:tableName]) return;
        
        
        if (models && [models isKindOfClass:[NSArray class]] && models.count > 0)
        {
            
            IGLock(_sqlLock);
            NSMutableString *sqliteString = [NSMutableString stringWithFormat:@"insert into %@ (", tableName];
            NSArray *columns = [self getAllColumnsFromTable:tableName dbQueue:self.dbQueue isIncludingPrimaryKey:NO];
            NSMutableArray *values = [NSMutableArray array];
            for (int index = 0; index < columns.count; index++) {
                [values addObject:@"?"];
            }
            [sqliteString appendFormat:@"%@) values (%@)", [columns componentsJoinedByString:@","], [values componentsJoinedByString:@","]];
            IGUnLock(_sqlLock);
            
            
            for (id model in models) {
                
                __block BOOL isSuccess;
                NSArray *arguments = [self getValuesFromModel:model columns:columns];
                [_dbQueue inDatabase:^(FMDatabase * _Nonnull db) {
                    isSuccess = [db executeUpdate:sqliteString withArgumentsInArray:arguments];
                }];
                if (!isSuccess) {
                    debugLog(@"----插入失败----");
                }
            }
        }
        else {
            
            debugLog(@"----插入数据的数据源有误----");
        }
        
    }
    
    
    
    
    
    
    #pragma mark - 删除数据
    
    - (BOOL)deleteFromTable:(NSString * _Nonnull)tableName whereParameters:(IGParameters *)parameters {
    
        if (![self checkTableName:tableName]) return NO;
        
        
        if (![self isStringVaild:parameters.whereParameters]) {
            debugLog(@"-----单条删除没有条件------");
            return NO;
        }
        
        
        IGLock(_sqlLock);
        NSMutableString *sqliteString = [NSMutableString stringWithFormat:@"delete from %@", tableName];
        if (parameters) {
            [sqliteString appendFormat:@" where %@", parameters.whereParameters];
        }
        IGUnLock(_sqlLock);
        
        
        __block BOOL isSuccess ;
        [_dbQueue inDatabase:^(FMDatabase * _Nonnull db) {
            isSuccess = [db executeUpdate:sqliteString];
        }];
        if (!isSuccess) {
            debugLog(@"----删除失败----");
        }
        return isSuccess;
    }
    
    
    - (BOOL)deleteAllDataFromTable:(NSString * _Nonnull)tableName {
    
        if (![self checkTableName:tableName]) return NO;
        
    
        IGLock(_sqlLock);
        NSMutableString *sqliteString = [NSMutableString stringWithFormat:@"delete from %@", tableName];
        IGUnLock(_sqlLock);
        
        
        __block BOOL isSuccess ;
        [_dbQueue inDatabase:^(FMDatabase * _Nonnull db) {
            isSuccess = [db executeUpdate:sqliteString];
        }];
        if (!isSuccess) {
            debugLog(@"----删除失败----");
        }
        return isSuccess;
        
    }
    
    
    
    
    
    
    
    
    #pragma mark - 更改数据
    
    - (BOOL)updateTable:(NSString * _Nonnull)tableName dictionary:(NSDictionary * _Nonnull)dictionary whereParameters:(IGParameters *)parameters {
    
        if (![self checkTableName:tableName]) return NO;
        
        if (dictionary.allKeys.count <= 0) {
            debugLog(@"----要更新的数据不能为nil----");
            return NO;
        }
        
    
        IGLock(_sqlLock);
        NSMutableString *sqliteString = [NSMutableString stringWithFormat:@"update %@ set ", tableName];
        NSMutableArray *values = [NSMutableArray array];
        for (NSString *key in dictionary) {
            if ([key isEqualToString:ig_primary_key]) {
                continue;
            }
            [sqliteString appendFormat:@"%@ = ? ", key];
            [values addObject:dictionary[key]];
        }
        IGUnLock(_sqlLock);
    
        
        if (values.count > 0) {
            
            if ([self isStringVaild:parameters.whereParameters]) {
                [sqliteString appendFormat:@"where %@", parameters.whereParameters];
            } else {
                debugLog(@"sql语句当中,where后面的参数为nil");
                [sqliteString deleteCharactersInRange:NSMakeRange(sqliteString.length-1, 1)];
            }
    
            __block BOOL isSuccess;
            [_dbQueue inDatabase:^(FMDatabase * _Nonnull db) {
                isSuccess = [db executeUpdate:sqliteString withArgumentsInArray:values];
            }];
            
            if (!isSuccess) {
                debugLog(@"-----更改数据------");
            }
            return isSuccess;
            
        } else {
    
            debugLog(@"要更新的数据不能仅仅含有主键");
            return NO;
        }
    }
    
    
    
    
    
    #pragma mark - 查询数据
    
    - (NSArray *)queryFromTable:(NSString * _Nonnull)tableName model:(Class _Nonnull)modelClass whereParameters:(IGParameters *)parameters {
    
        
        if (![self checkTableName:tableName]) return nil;
        
    
        IGLock(_sqlLock);
        NSMutableString *sqliteString = [NSMutableString stringWithFormat:@"select * from %@", tableName];
        if (parameters && [self isStringVaild:parameters.whereParameters]) {
            [sqliteString appendFormat:@" where %@", parameters.whereParameters];
        }
        IGUnLock(_sqlLock);
        
        
        __block NSMutableArray *array = [NSMutableArray array];
        NSDictionary *properties = [self getPropertiesWithModel:modelClass];
        
        [_dbQueue inDatabase:^(FMDatabase * _Nonnull db) {
            
            FMResultSet *res = [db executeQuery:sqliteString];
            while ([res next]) {
                NSMutableDictionary *dict = [NSMutableDictionary dictionary];
                for (NSString *key in properties) {
                    NSString *type = properties[key];
                    // 根据数据类型从数据库当中获取数据
                    if ([type isEqualToString:ig_sql_text]) {
                        // 字符串
                        dict[key] = [res stringForColumn:key] ? : @"";
                    } else if ([type isEqualToString:ig_sql_integer]) {
                        // 整型
                        dict[key] = @([res longLongIntForColumn:key]);
                    } else if ([type isEqualToString:ig_sql_real]) {
                        // 浮点型
                        dict[key] = @([res doubleForColumn:key]);
                    } else if ([type isEqualToString:ig_sql_blob]) {
                        // 二进制
                        id value = [res dataForColumn:key];
                        if (value) {
                            dict[key] = value;
                        }
                    }
                }
                [array addObject:dict];
            }
            
        }];
        
        return (array.count > 0 ? array : nil);
        
    }
    
    
    
    
    #pragma mark - 除去增删改查之外常用的功能
    
    
    
    /**
     *  表是否存在
     *  @param tableName    表的名字
     *  @return             表是否存在
     */
    - (BOOL)existTable:(NSString * _Nonnull)tableName {
    
        if (![self checkTableName:tableName]) return NO;
        
        __block BOOL isExist;
        [_dbQueue inDatabase:^(FMDatabase * _Nonnull db) {
             FMResultSet *res = [db executeQuery:@"select count(*) as 'count' from sqlite_master where type ='table' and name = ?", tableName];
            while ([res next]) {
                NSInteger count = [res intForColumn:@"count"];
                isExist = ((count == 0) ? NO : YES);
            }
        }];
        
        return isExist;
    
    }
    
    
    /**
     *  为一个表增加字段
     *  @param tableName    表的名字
     *  @param column       要增加的字段
     *  @param type         增加的字段类型
     *  @return             是否添加成功
     */
    - (BOOL)alterTable:(NSString * _Nonnull)tableName column:(NSString * _Nonnull)column type:(IGFMDBValueType)type {
    
        if (![self checkTableName:tableName]) return NO;
        
        if (![self isStringVaild:column]) {
            debugLog(@"---要新增的column必须是字符串,且不能为nil-----");
            return NO;
        }
        
        
        IGLock(_sqlLock);
        NSString *typeString = nil;
        switch (type) {
            case IGFMDBValueTypeString:
                typeString = ig_sql_text;
                break;
            case IGFMDBValueTypeInteger:
                typeString = ig_sql_integer;
                break;
            case IGFMDBValueTypeFloat:
                typeString = ig_sql_real;
                break;
            case IGFMDBValueTypeData:
                typeString = ig_sql_blob;
                break;
            default:
                typeString = @"";
                break;
        }
        NSString *sqliteString = [NSString stringWithFormat:@"alter table %@ add column %@ %@", tableName, column, typeString];
        IGUnLock(_sqlLock);
        
        __block BOOL isSuccess;
        [_dbQueue inDatabase:^(FMDatabase * _Nonnull db) {
            isSuccess = [db executeUpdate:sqliteString];
        }];
        
        return isSuccess;
        
        
    }
    
    
    /**
     *  删除一张表
     *  @param tableName    表的名字
     *  @return             是否删除成功
     */
    - (BOOL)dropTable:(NSString * _Nonnull)tableName {
    
        if (![self checkTableName:tableName]) return NO;
        
        IGLock(_sqlLock);
        NSString *sqliteString = [NSString stringWithFormat:@"drop table %@", tableName];
        IGUnLock(_sqlLock);
        
        __block BOOL isSuccess ;
        [_dbQueue inDatabase:^(FMDatabase * _Nonnull db) {
            isSuccess = [db executeUpdate:sqliteString];
        }];
        
        return isSuccess;
        
    }
    
    /**
     *  获取某一个表中所有的字段名
     *  @param tableName    表的名字
     *  @return             所有字段名
     */
    - (NSArray<NSString *> *)getAllColumnsFromTable:(NSString * _Nonnull)tableName {
        
        if (![self checkTableName:tableName]) return nil;
        
        return [self getAllColumnsFromTable:tableName dbQueue:self.dbQueue isIncludingPrimaryKey:YES];
    }
    
    
    /**
     *  获取表中有多少条数据
     *  @param tableName    表的名字
     *  @param parameters   参数,IGParameters决定了sql语句"where"后面的参数
     *  @return             数据的个数
     */
    - (long long int)numberOfItemsFromTable:(NSString * _Nonnull)tableName whereParameters:(IGParameters * _Nullable)parameters {
    
        if (![self checkTableName:tableName]) return 0;
        
        IGLock(_sqlLock);
        NSMutableString *sqliteString = [NSMutableString stringWithFormat:@"select count(*) as 'count' from %@", tableName];
        if (parameters && [self isStringVaild:parameters.whereParameters]) {
            [sqliteString appendFormat:@" where %@", parameters.whereParameters];
        }
        IGUnLock(_sqlLock);
        
        __block long long count ;
        [_dbQueue inDatabase:^(FMDatabase * _Nonnull db) {
            FMResultSet *res = [db executeQuery:sqliteString];
            while ([res next]) {
                count = [res longLongIntForColumn:@"count"];
            }
        }];
        return count;
    
    }
    
    
    
    /**
     *  数学相关操作
     *  @param type         数学运算的type
     *  @param tableName    表的名字
     *  @param parameters   参数,IGParameters决定了sql语句"where"后面的参数
     *  @return             计算的值
     */
    - (double)numberWithMathType:(IGFMDBMathType)type table:(NSString * _Nonnull)tableName column:(NSString * _Nonnull)column whereParameters:(IGParameters * _Nullable)parameters {
    
    
        if (![self checkTableName:tableName]) return 0;
        
        
        if (![self isStringVaild:parameters.whereParameters]) {
            debugLog(@"---要新增的column必须是字符串,且不能为nil----");
            return 0.0;
        }
        
        IGLock(_sqlLock);
        NSMutableString *sqliteString = nil;
        NSString *operation = nil;
        switch (type) {
            case IGFMDBMathTypeSum:
                operation = @"sum";
                break;
            case IGFMDBMathTypeAvg:
                operation = @"avg";
                break;
            case IGFMDBMathTypeMax:
                operation = @"max";
                break;
            case IGFMDBMathTypeMin:
                operation = @"min";
                break;
            default:
                break;
        }
        if ([self isStringVaild:operation]) {
            sqliteString = [NSMutableString stringWithFormat:@"select %@(%@) %@Count from %@", operation, column, operation, tableName];
        } else {
            debugLog(@"----不支持当前运算----");
        }
        if (parameters) {
            [sqliteString appendFormat:@" where %@", parameters.whereParameters];
        }
        IGUnLock(_sqlLock);
        
        __block double value = 0.0;
        [_dbQueue inDatabase:^(FMDatabase * _Nonnull db) {
            FMResultSet *res = [db executeQuery:sqliteString];
            while ([res next]) {
                value = [res doubleForColumn:[NSString stringWithFormat:@"%@Count", operation]];
            }
        }];
        
        return value;
        
    
    }
    
    
    
    
    #pragma mark - 数据库相关操作
    
    // 获取数据库里的所有元素
    - (NSArray<NSString *> *)getAllColumnsFromTable:(NSString *)tableName dbQueue:(FMDatabaseQueue *)dbQueue isIncludingPrimaryKey:(BOOL)isIncluding {
    
        __block  NSMutableArray *columns = [NSMutableArray array];
        
        [dbQueue inDatabase:^(FMDatabase * _Nonnull db) {
            FMResultSet *res = [db getTableSchema:tableName];
            while ([res next]) {
                NSString *columnName = [res stringForColumn:@"name"];
                if ([columnName isEqualToString:ig_primary_key] && !isIncluding) {
                     continue;
                }
                [columns addObject:columnName];
            }
        }];
        return columns;
    }
    
    
    
    
    
    
    #pragma mark - Private Method
    
    /**
     *  基于runtime获取model的所有属性以及类型
     *  根据传入的ModelClass去获取所有的属性的key以及类型type,返回值的字典的key就是modelClass的属性,value就是modelClass的属性对应的type
     */
    - (NSDictionary *)getPropertiesWithModel:(Class)modelClass {
        NSMutableDictionary *dict = [NSMutableDictionary dictionary];
    
        unsigned int count;
        objc_property_t *propertyList = class_copyPropertyList(modelClass, &count);
        for (int index = 0; index < count; index++) {
            objc_property_t property = propertyList[index];
            NSString *key = [NSString stringWithFormat:@"%s", property_getName(property)];
            NSString *type = nil;
            NSString *attributes = [NSString stringWithFormat:@"%s", property_getAttributes(property)];
    
            if ([attributes hasPrefix:@"T@\"NSString\""]) {
                type = ig_sql_text;
            } else if ([attributes hasPrefix:@"Tf"] || [attributes hasPrefix:@"Td"]) {
                type = ig_sql_real;
            } else if ([attributes hasPrefix:@"T@\"NSData\""]) {
                type = ig_sql_blob;
            } else if ([attributes hasPrefix:@"Ti"] || [attributes hasPrefix:@"TI"] || [attributes hasPrefix:@"Tl"] || [attributes hasPrefix:@"TL"] || [attributes hasPrefix:@"Tq"] || [attributes hasPrefix:@"TQ"] || [attributes hasPrefix:@"Ts"] || [attributes hasPrefix:@"TS"] || [attributes hasPrefix:@"TB"] || [attributes hasPrefix:@"T@\"NSNumber\""]) {
                type = ig_sql_integer;
            }
    
            if (type) {
                [dict setObject:type forKey:key];
            } else {
                debugLog(@"---%@----",[NSString stringWithFormat:@"不支持的属性:key = %@, attributes = %@", key, attributes]);
            }
        }
    
        free(propertyList);
    
        return dict;
    }
    
    // 根据keys获取到model里面的所有values
    - (NSArray *)getValuesFromModel:(id _Nonnull)model columns:(NSArray *)columns {
        NSMutableArray *array = [NSMutableArray array];
        for (NSString *column in columns) {
            id value = [model valueForKey:column];
            [array addObject:value ? : @""];
        }
        return array;
    }
    
    
    // 加锁
    void IGLock(dispatch_semaphore_t semaphore) {
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    }
    
    // 解锁
    void IGUnLock(dispatch_semaphore_t semaphore) {
        dispatch_semaphore_signal(semaphore);
    }
    
    
    @end
    
    

    IGParameters.h 文件

    #import <Foundation/Foundation.h>
    
    NS_ASSUME_NONNULL_BEGIN
    
    // 参数相关的关系
    typedef NS_ENUM(NSUInteger, IGParametersRelationType) {
        IGParametersRelationTypeEqualTo,               // 数学运算@"=",等于
        IGParametersRelationTypeUnequalTo,             // 数学运算@"!=",不等于
        IGParametersRelationTypeGreaterThan,           // 数学运算@">",大于
        IGParametersRelationTypeGreaterThanOrEqualTo,  // 数学运算@">=",大于等于
        IGParametersRelationTypeLessThan,              // 数学运算@"<",小于
        IGParametersRelationTypeLessThanOrEqualTo,     // 数学运算@"<=",小于等于
        IGParametersRelationTypeLike,                  // 字符串运算@"like",模糊查询   这个又有左右模糊之分
    };
    
    // 排序顺序
    typedef NS_ENUM(NSUInteger, IGParametersOrderType) {
        IGParametersOrderTypeAsc,                      // 升序
        IGParametersOrderTypeDesc,                     // 降序
    };
    
    
    
    @interface IGParameters : NSObject
    
    #pragma mark - sql语句当中为where之后的条件增加参数
    
    /**
     *  筛选条件的数量限制
     */
    @property (nonatomic, assign) NSInteger limitCount;
    
    /**
     *  and(&&,与)操作
     *  @param column           数据库中表的key值
     *  @param value            column值对应的value值
     *  @param relationType     column与value之间的关系
     *  比如只执行[andWhere:@"age" value:18 relationType:IGParametersRelationTypeGreaterThan],那么where后面的参数会变成"age > 18"
     */
    - (void)andWhere:(NSString * _Nonnull)column value:(id _Nonnull)value relationType:(IGParametersRelationType)relationType;
    
    
    /**
     *  or(||,或)操作
     *  @param column           数据库中表的key值
     *  @param value            column值对应的value值
     *  @param relationType     column与value之间的关系
     */
    - (void)orWhere:(NSString * _Nonnull)column value:(id _Nonnull)value relationType:(IGParametersRelationType)relationType;
    
    
    /**
     *  设置排序结果
     *  @param column           排序的字段
     *  @param orderType        排序选择,有升序和降序
     *  比如执行[ orderByColumn:@"Id" orderType:IGParametersOrderTypeAsc],那么对应的sql语句就是@"order by Id asc",意思就是根据"Id"来进行升序排列
     */
    - (void)orderByColumn:(NSString * _Nonnull)column orderType:(IGParametersOrderType)orderType;
    
    
    /**
     *  sql语句的参数,也就是sql语句当中,where之后的参数.
     *  值得一提的是,如果设置了这个参数,那么在属性whereParameters上面的方法都无效
     *  如果不设置这个参数,那么调用此属性的get方法则会获取到以上的方法所形成的sql语句
     */
    @property (nonatomic, copy)   NSString *whereParameters;
    
    
    
    
    
    
    
    
    
    
    @end
    
    NS_ASSUME_NONNULL_END
    
    

    IGParameters.m 文件

    #import "IGParameters.h"
    
    @interface IGParameters ()
    
    @property (nonatomic, strong) NSMutableArray<NSString *> *andParameters;    // and参数
    @property (nonatomic, strong) NSMutableArray<NSString *> *orParameters;     // or参数
    @property (nonatomic, copy)   NSString *orderString;                        // 排序语句
    
    
    @end
    
    @implementation IGParameters
    
    - (NSMutableArray<NSString *> *)andParameters {
        if (!_andParameters) {
            _andParameters = [NSMutableArray array];
        }
        
        return _andParameters;
    }
    
    - (NSMutableArray<NSString *> *)orParameters {
        if (!_orParameters) {
            _orParameters = [NSMutableArray array];
        }
        
        return _orParameters;
    }
    
    - (NSString *)whereParameters {
        if (_whereParameters) {
            return _whereParameters;
        } else {
            NSMutableString *string = [NSMutableString string];
            NSString *andString = [self.andParameters componentsJoinedByString:@" and "];
            NSString *orString  = [self.orParameters componentsJoinedByString:@" or "];
            if (andString && andString.length > 0) {
                [string appendFormat:@"%@", andString];
            }
            
            if (orString && orString.length > 0) {
                [string appendFormat:@"%@%@", (string.length > 0 ? @" or " : @""), orString];
            }
            
            if (self.orderString) {
                [string appendFormat:@" %@", self.orderString];
            }
            
            if (self.limitCount > 0) {
                [string appendFormat:@" limit %ld", (long)self.limitCount];
            }
            
            return (NSString *)(string.length > 0 ? string : nil);
        }
    }
    
    
    
    
    
    
    /**
     *  and(&&,与)操作
     */
    - (void)andWhere:(NSString * _Nonnull)column value:(id _Nonnull)value relationType:(IGParametersRelationType)relationType {
        
        if ([value isKindOfClass:[NSString class]] && relationType != IGParametersRelationTypeLike) {
            
            value = [NSString stringWithFormat:@"'%@'",value]; //如果是字符串, 外面会加 单引号
        }
        
        
        NSString *string = nil;
        switch (relationType) {
            case IGParametersRelationTypeEqualTo:
                string = [NSString stringWithFormat:@"%@ = %@", column, value];
                break;
            case IGParametersRelationTypeUnequalTo:
                string = [NSString stringWithFormat:@"%@ != %@", column, value];
                break;
            case IGParametersRelationTypeGreaterThan:
                string = [NSString stringWithFormat:@"%@ > %@", column, value];
                break;
            case IGParametersRelationTypeGreaterThanOrEqualTo:
                string = [NSString stringWithFormat:@"%@ >= %@", column, value];
                break;
            case IGParametersRelationTypeLessThan:
                string = [NSString stringWithFormat:@"%@ < %@", column, value];
                break;
            case IGParametersRelationTypeLessThanOrEqualTo:
                string = [NSString stringWithFormat:@"%@ <= %@", column, value];
                break;
            case IGParametersRelationTypeLike:
            {
                //左右都要模糊 %%%@%%
                //左模糊 %%%@
                //右模糊 %@%%
                string = [NSString stringWithFormat:@"%@ like '%@%%' ", column, value];
            }
                break;
            default:
                break;
        }
        if (string) {
            [self.andParameters addObject:string];
        }
    }
    
    
    
    
    /**
     *  or(||,或)操作
     */
    - (void)orWhere:(NSString * _Nonnull)column value:(id _Nonnull)value relationType:(IGParametersRelationType)relationType {
        
        NSString *string = nil;
        switch (relationType) {
            case IGParametersRelationTypeEqualTo:
                string = [NSString stringWithFormat:@"%@ = %@", column, value];
                break;
            case IGParametersRelationTypeUnequalTo:
                string = [NSString stringWithFormat:@"%@ != %@", column, value];
                break;
            case IGParametersRelationTypeGreaterThan:
                string = [NSString stringWithFormat:@"%@ > %@", column, value];
                break;
            case IGParametersRelationTypeGreaterThanOrEqualTo:
                string = [NSString stringWithFormat:@"%@ >= %@", column, value];
                break;
            case IGParametersRelationTypeLessThan:
                string = [NSString stringWithFormat:@"%@ < %@", column, value];
                break;
            case IGParametersRelationTypeLessThanOrEqualTo:
                string = [NSString stringWithFormat:@"%@ <= %@", column, value];
                break;
            default:
                break;
        }
        if (string) {
            [self.orParameters addObject:string];
        }
    }
    
    
    
    
    /**
     *  设置排序结果
     */
    - (void)orderByColumn:(NSString * _Nonnull)column orderType:(IGParametersOrderType)orderType {
        
        if (orderType == IGParametersOrderTypeAsc) {
            self.orderString = [NSString stringWithFormat:@"order by %@ asc", column];
        } else if (orderType == IGParametersOrderTypeDesc) {
            self.orderString = [NSString stringWithFormat:@"order by %@ desc", column];
        }
    }
    
    @end
    
    

    相关文章

      网友评论

          本文标题:iOS 本地数据库持久化存储 (包含版本升级和切换账号的问题)

          本文链接:https://www.haomeiwen.com/subject/aungfqtx.html