FMDB封装,用OC方法存储model,不用写SQL语句。使用runtime获取对象属性

在实际开发中,数据库存储只会用到那几个增删改查SQL语句,不会用到多表查询啊,多种条件增删改啊。所以写这个东西的目的,是把这几个SQL语句封装起来,在开发中操作数据库就省去写了SQL语句的麻烦。可能中间有些写得不够好,有兴趣的大虾过来指点指点小弟。


我们保存model,无非就是把所有的model装到一个数据里面,把数据直接塞进数据库里面即可。
创建SQL语句要得到model的键和值。这里使用RunTime方法来获取model的属性。下面是核心代码

    // model的属性作为表的列名
    // 可以通过RunTime的方法来获取model的属性

    // model属性的个数
    unsigned int propertyCount = 0;

    // 通过运行时获取model的属性 这里要导入 #import <objc/runtime.h>
    objc_property_t *propertys = class_copyPropertyList([model class], &propertyCount);

    for (int i = 0; i < propertyCount; i ++) {
        // 取出元素
        objc_property_t property = propertys[i];
        // 得到的是char*型的
        const char *propertyName = property_getName(property);
        // 转化
        NSString *OCString = [NSString stringWithUTF8String:propertyName];
//        NSLog(@"%@", OCString);

操作数据库分几个步骤
1. 创建数据库

……
#define  FMDBManager  [LJXFMDBManager ShareFMDBManager]
……

  // 一般保存到cache目录里面
    NSString *dataPath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
    dataPath = [dataPath stringByAppendingPathComponent:@"MyDB.sqlite"];
    // 创建数据库
    [FMDBManager createDataBaseWithPath:dataPath];

为了让这过程中都能看到数据库的东西,我们用SQLiteManager去下载SQLiteManager
打开数据库文件,首先我们得找到这个文件。我在这文章里有打开真机caches文件里的方法。
http://blog.csdn.net/u010971348/article/details/53290523

打开caches目录后,可以看到创建的sqlite文件,安装了SQLiteManager的话,默认打开方式就是SQLiteManager了。又击它,没创表的时候,当然是空的,我这里创建了表,

创建表代码, 为了方便操作,传一个主键过去。(不设置主键的我也写过了,结果,操作麻烦,比如插入会有重复的model,要解决这个问题,就要在插入之前查一下表,看要插入的model存不存在,存在的话就替换,不存在的话就插入,这样子太浪更内存了)

调用方法
    NSString *tableName =_tableName.text;
    Class modelClass = [CheckinModel class];
    if ([FMDBManager createTableWithName:tableName model:modelClass primaryKey:@"flightCode"]) {
      [self showMessage:@"创表成功"];
    } else {
        [self showMessage:@"创表失败"];
    }

/**
 创建表

 @param tableName 表名
 @param primaryKey 设置主键以便操作
 @return 创建表是否成功
 */
- (BOOL)createTableWithName:(NSString *)tableName model:(Class)model primaryKey:(NSString *)primaryKey;


- (BOOL)createTableWithName:(NSString *)tableName model:(Class)model primaryKey:(NSString *)primaryKey
{
    if (self.dataBase) {
        // 打开数据库
        [self.dataBase open];
        // 判断表是否存在
        BOOL success = [self.dataBase tableExists:tableName];

        if (success) {
            // 存在就不用创建了,操作完成。记得关闭数据库
            [self.dataBase close];
            return YES;
        } else {
            // 不存在的话,
            // 创建SQL语句;
            NSString *SQLString = [self createTableSQLString:model tableName:tableName primaryKey:primaryKey];
            if ([self.dataBase executeUpdate:SQLString]) {
                // 创建表成功,关闭
                [self.dataBase close];
                return YES;
            } else {
                return NO;
            }
        }
    } else {
        NSLog(@"数据库不存在");
        return NO;
    }
}

// 创表语句
- (NSString *)createTableSQLString:(id)model tableName:(NSString *)tableName primaryKey:(NSString *)primaryKey
{
    // 为了提交表数据的容错性,都用text 类型
    NSString *sqliteString = [NSString stringWithFormat:@"create table if not exists %@ (%@ text primary key", tableName, primaryKey];

    // model的属性作为表的列名
    // 可以通过RunTime的方法来获取model的属性

    // model属性的个数
    unsigned int propertyCount = 0;

    // 通过运行时获取model的属性 这里要导入 #import <objc/runtime.h>
    objc_property_t *propertys = class_copyPropertyList([model class], &propertyCount);

    for (int i = 0; i < propertyCount; i ++) {
        // 取出元素
        objc_property_t property = propertys[i];
        // 得到的是char*型的
        const char *propertyName = property_getName(property);
        // 转化
        NSString *OCString = [NSString stringWithUTF8String:propertyName];
//        NSLog(@"%@", OCString);
        // 如果是主键的话,就不用拼接上去了,因为一开始就拼了
        if (![OCString isEqualToString:primaryKey]) {
            // 拼接
            if (i == 0) {
                sqliteString = [sqliteString stringByAppendingString:[NSString stringWithFormat:@"%@ text", OCString]];
            } else {
                sqliteString = [sqliteString stringByAppendingString:[NSString stringWithFormat:@", %@ text", OCString]];
            }
        }

    }
    sqliteString = [sqliteString stringByAppendingString:@")"];

    return sqliteString;
}

创建表成功后,我们可以在那个sqlite文件里面看到这些东西,可以看到,表的列名就是model的属性名,除了前面的那个rowid是自己本来就有的

接下来就是插入model了。

  NSMutableArray *muta = [NSMutableArray array];
    NSString *path = [[NSBundle mainBundle] pathForResource:@"checkin" ofType:@"json"];
    NSData *jsData = [NSData dataWithContentsOfFile:path];
    NSDictionary *dataDic = [NSJSONSerialization JSONObjectWithData:jsData options:NSJSONReadingMutableContainers error:nil];
    NSArray *dataArray = dataDic[@"data"];
    for (NSDictionary *subDic in dataArray) {
        CheckinModel *model = [CheckinModel initWithDic:subDic];
        [muta addObject:model];
    }
    /// 插入
    if ([FMDBManager insterModelArray:muta toTable:_tableName.text]) {
        [self showMessage:@"插入成功"];
        NSArray *all = [FMDBManager searchAllModel:[CheckinModel class] tableName:_tableName.text];
        [_dataArray removeAllObjects];
        [_dataArray addObjectsFromArray:all];
        [self.tableView reloadData];
        NSLog(@"个数%ld", all.count);
    } else {
        [self showMessage:@"插入失败"];
    }

/**
  插入 模型 数组

 @param modelArray 模型数组
 @param tableName 表名
 @return 插入是否成功
 */
 .h里面
- (BOOL)insterModelArray:(NSArray *)modelArray toTable:(NSString *)tableName;

.m里面
// 插入一组
- (BOOL)insterModelArray:(NSArray *)modelArray toTable:(NSString *)tableName
{
    if (self.dataBase) {
        // 凡事先打开数据库
        [self.dataBase open];

        if ([self.dataBase tableExists:tableName]) {
            NSString *sqlString = @"";
            for (NSInteger i = 0; i < modelArray.count; i ++) {
                // 拼接sql语句
                NSString *subString = [self insertSQLStringWith:modelArray[i] tableName:tableName];
                if (i == 0) {
                    sqlString = [sqlString stringByAppendingString:subString];
                } else {
                    sqlString = [sqlString stringByAppendingFormat:@"; %@", subString];
                }
            }

            // 多行执行
            BOOL success = [self.dataBase executeStatements:sqlString];
            [self.dataBase close];
            return success;
        }else {
            // 表不存在
            NSLog(@"插入失败,表不存在");
            [self.dataBase close];
            return NO;
        }
    } else {
        // 数据库都不存在,先去创建数据库吧
        NSLog(@"数据库不存在");
        return NO;
    }
}

// 插入数据语句
- (NSString *)insertSQLStringWith:(id)model tableName:(NSString *)tableName
{
    NSString *sqliteString = [NSString stringWithFormat:@"insert or replace into %@ (", tableName];
    // 一般是以model的属性作为表的列名
    // 可以通过RunTime的方法来获取model的属性

    // model属性的个数
    unsigned int propertyCount = 0;

    // 通过运行时获取model的属性 这里要导入 #import <objc/runtime.h>
    objc_property_t *propertys = class_copyPropertyList([model class], &propertyCount);
    // 装属性的数组
    NSMutableArray *keyArray = [NSMutableArray array];
    for (int i = 0; i < propertyCount; i ++) {
        // 取出元素
        objc_property_t property = propertys[i];
        const char *propertyName = property_getName(property);
        // 转化
        NSString *OCString = [NSString stringWithUTF8String:propertyName];

        NSString *valueString = [NSString stringWithFormat:@"%@",[model valueForKey:OCString]];
        if (valueString.length > 0) {
            // 如果model的属性没有值的话,那就不加操作那列值了
            // 保存键
            [keyArray addObject:OCString];
            // 拼接
            if (i == 0) {
                sqliteString = [sqliteString stringByAppendingString:[NSString stringWithFormat:@"'%@'",OCString]];
            } else {
                sqliteString = [sqliteString stringByAppendingString:[NSString stringWithFormat:@", '%@'", OCString]];
            }
        }
    }
    sqliteString = [sqliteString stringByAppendingString:@") values ("];

    // 值
    for (int i = 0; i < keyArray.count; i ++) {
        NSString *key = keyArray[i];
        NSString *keyString;
        // 拼接 记得加单引号
        if (i == 0) {
            keyString = [NSString stringWithFormat:@"'%@'", [model valueForKey:key]];
        } else {
              keyString = [NSString stringWithFormat:@", '%@'", [model valueForKey:key]];
        }

        sqliteString = [sqliteString stringByAppendingString:keyString];
    }
    //
    sqliteString = [sqliteString stringByAppendingString:@")"];
    return sqliteString;
}

执行完后,再看看sqlite里面

用得最多的就是这个插入方法了,因为他也是更新方法,你随便插入model,不过要是那一类model,如果里面有相同的话,他会直接替换掉,我这里用的SQL语句是
insert or replace into 避免他有重复的model。
还有更他的方法

**
 查询某条数据所在的model。比如学号(key)为2001(vlaue)的model

 @param model 所查model类型
 @param keyString 所查的键
 @param valueString 所查的值
 @param tableName 所查的表
 @return 返回的model数组
 */
- (NSArray *)searchModel:(Class)model WithKey:(NSString *)keyString value:(NSString *)valueString inTable:(NSString *)tableName;

/**
 查询所有的model

 @param modelClass 所查的model类型
 @param tableName 表名
 @return 所有的model
 */
- (NSArray *)searchAllModel:(Class)modelClass tableName:(NSString *)tableName;


#pragma mark - 删除

/**
 删除表中所有的数据, 不删除表, 但不删除列名

 @param tableName 表名
 @return 是否删除成功
 */
- (BOOL)truncateTable:(NSString *)tableName;

/**
 删除整个表

 @param tableName 表名
 @return 是否删除成功
 */
- (BOOL)dropTable:(NSString *)tableName;


/**
 删除指定条件的model
 如:删除学号 为 2002的model
 @param columnName 指定列名
 @param columnValue 指定列值
 @param tableName 表名
 @return 删除成功与否
 */
- (BOOL)deleteModelColumnName:(NSString *)columnName columnValue:(NSString *)columnValue tableName:(NSString *)tableName;

都写进去了,demo请移步gitHub

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章