美文网首页
一行代码搞定iOS端数据库存储

一行代码搞定iOS端数据库存储

作者: Everdinner | 来源:发表于2018-03-06 12:16 被阅读27次

    正常情况下,当我们需要进行数据库存储操作时,需要进行以下操作:
    1、书写数据库语句创建表

    CREATE TABLE IF NOT EXISTS TableName
    

    2、确定数据库中需要插入字段,然后书写数据库语句进行插入操作

    INSERT INTO TableName (name1, name2,...) Values (?, ?,...)
    

    真正的项目中我们需要做的操作可能会更多,比如需要判断表是否存在、要插入的字段是否存在等一系列判断,本来好好写代码逻辑的人怎么就被拉去写数据库语句出不来了(心疼自己几分钟)!

    当然,一个完整的项目里面不可能只有这些操作,应该包括数据库的增(Insert)、删(delete)、改(update)、查(select)四大操作,数据库查找还包括条件筛选语句,如果你一行一行的写完这些语句,会发现整个人都不好了,难道就没有一个更好地解决办法吗?

    我们应该都对数据模型化不陌生,各种数据转模型的第三方库如MJExtension、YYModel应有尽有,我们只需要一行代码即可完成数据模型化的操作,比如MJExtension中的数组转模型:

    NSArray *userArray = [MJUser mj_objectArrayWithKeyValuesArray:dictArray];
    

    得到的就是一个模型数组,想象一下数据库的所有操作也都用一行代码来完成是什么感觉?

    //Insert
    for (LYTeacher *teacher in self.teachers) {
            [LYFMDBTool ly_insertDBWithModel:teacher category:FMDB_TEACHER];
    }
    
    //Delete
    [LYFMDBTool ly_deleteDBWithModel:teacher
                                               category:FMDB_TEACHER
                                           propertyName:@"teacherId"];
    
    //Update
    LYTeacher *teacher = self.teachers[arc4random_uniform(20)];
    teacher.name = [teacher.name stringByAppendingString:@"updated"];
    BOOL success = [LYFMDBTool ly_updateDBWithModel:teacher
                                               category:FMDB_TEACHER
                                           propertyName:@"teacherId"];
    
    //Select
    NSArray *result = [LYFMDBTool ly_selectFromDBWithTableName:NSStringFromClass([LYTeacher class]) category:FMDB_TEACHER];
    
    //Filter Select
    //按照降序查找数据库中10条数据
    NSString *filter = @"ORDER BY teacherId DESC LIMIT 10";
        NSArray *result = [LYFMDBTool ly_selectFromDBWithTableName:NSStringFromClass([LYTeacher class]) category:FMDB_TEACHER filter:filter];
    

    基于模型对象的数据库增、删、改、查操作已经完成,而且我们并没有书写一句SQL语句,并且每当你为模型对象增加属性字段后执行插入或者更新操作,数据库中都会增加对应的字段。

    这其中的魔法操作便是runtime,我们通过运行时动态获取模型对象的所有属性字段将其存到数组中,然后再对每个属性进行字符串拼接动态生成数据库语句,最后执行对应的数据库语句即可,下面是对应的实现代码

    /**
     插入模型到指定表中
     @param model 模型名称(必须是模型名称)
     @param tableName 表名
     @param category  表后缀名(用来区别以同一个模型创建的不同表名)
     */
    + (BOOL)ly_insertDBWithModel:(NSObject *)model tableName:(NSString *)tableName category:(NSString *)category {
        if (!model) { return NO; }
        //动态拼接数据库表名,这里由模型名、表名和表名后缀组成一个真正的数据库表名
        NSString *className = NSStringFromClass(object_getClass(model));
        className = (tableName == nil) ? className : tableName;
        __block BOOL successed = YES;
        //判断对应表名的数据库是否创建成功
        if (![self ly_createTableName:className category:category]) {
            successed = NO;
            return successed;
        }
        tableName = [className stringByAppendingString:category];
        //获取模型的所有属性
        NSArray *propertyArr = [self ly_getPropertyList:className];
        //获取允许插入数据库中的属性
        NSArray *allowedProperties = [model.class ly_FMDBAllowedPropertyNames];
        //拼接数据库语句
        NSString *sql1 = [NSString stringWithFormat:@"INSERT INTO %@%@ ",className,category];
        __block NSMutableString *keyStr = [NSMutableString string];
        __block NSMutableString *valueStr = [NSMutableString string];
        __block NSMutableArray *argumentsArr = [NSMutableArray array];
        for (int i = 0; i < propertyArr.count; i++) {
            __block MJProperty *property = propertyArr[i];
            __block MJPropertyType *type = property.type;
            if (![allowedProperties containsObject:property.name]) {
                continue;
            }
            [_queue inDatabase:^(FMDatabase *db) {
                //如果对应的属性字段在表中不存在,则在表中增加对应字段
                if (![db columnExists:property.name inTableWithName:tableName]) {
                    NSString *alterStr = [NSString stringWithFormat:@"ALTER TABLE %@ ADD %@ TEXT",tableName,property.name];
                    BOOL added = [db executeUpdate:alterStr];
                    if (!added) {
                        successed = NO;
                    }
                }
                NSUInteger length = keyStr.length;
                (!length)?[keyStr appendFormat:@"%@",property.name]
                :[keyStr appendFormat:@",%@",property.name];
                (!length)?[valueStr appendString:@"?"]
                :[valueStr appendString:@",?"];
                id value = [model valueForKey:property.name];
                if (value) {
                    //由于数据库不支持数组、字典、模型对象的直接存储,因此我们将数组、字典、模型对象转json字符串存储
                    if ([self _isKeyTypeValidateJSON:type]) {
                        NSString *jsonStr = [self _switchValue:value toJSON:type];
                        [argumentsArr addObject:jsonStr ?:@""];
                    } else {
                        [argumentsArr addObject:value];
                    }
                } else {
                    [argumentsArr addObject:@""];
                }
            }];
            if (!successed) {
                return successed;
            }
        }
        NSString *sql = [NSString stringWithFormat:@"%@(%@) VALUES (%@)",sql1,keyStr,valueStr];
        
        [_queue inDatabase:^(FMDatabase *db) {
            if (![db executeUpdate:sql withArgumentsInArray:argumentsArr]) {
                successed = NO;
            }
        }];
        return successed;
    }
    

    数据库的更新和删除操作也是这个原理,这里我们采取了MJExtension的处理办法,默认为NSObject对象增加了一个LYFMDBKeyValue协议,我们继承自NSObject的类可以声明需要插入到数据库的属性字段,默认会取所有的属性进行插入操作

    @protocol LYFMDBKeyValue <NSObject>
    
    @optional
    
    /**
     允许存储到数据库的属性
     */
    + (nonnull NSArray *)ly_FMDBAllowedPropertyNames;
    
    @end
    
    @interface NSObject (LYFMDBKeyValue) <LYFMDBKeyValue>
    
    @end
    
    @implementation NSObject (LYFMDBKeyValue)
    
    static NSMutableArray *_allowedProperties;
    + (NSArray *)ly_FMDBAllowedPropertyNames {
        if (_allowedProperties.count) {
            return _allowedProperties;
        }
        NSMutableArray *propertyList =  [LYFMDBTool ly_getPropertyList:NSStringFromClass([self class])];
        if (!_allowedProperties) {
            _allowedProperties = [NSMutableArray array];
        }
        for (MJProperty *property in propertyList) {
            [_allowedProperties addObject:property.name];
        }
        return _allowedProperties;
    }
    
    @end
    

    以上就是所有的实现原理,附上github链接地址:https://github.com/LustySwimmer/LYFMDBManager

    说明:
    1、该工具基于FMDB进行二次封装操作;
    2、数据模型化采用的是MJExtension进行转换处理;
    3、支持cocoapod导入
    4、数据模型要有一个唯一值得属性字段,可以使用时间作区分,也可以是其他不同的数字或者字符串
    注意点:
    不支持NSUInteger类型存储,最好使用NSString进行存储操作;
    在使用过程中暂时没有发现其他问题,如果有发现问题,欢迎提issue,如果有更好的建议和方法,欢迎指教!

    相关文章

      网友评论

          本文标题:一行代码搞定iOS端数据库存储

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