在使用这些代码前,我们进行了以下的约定:
1. sqlite的数据库表名直接使用实体类的类名;
2. sqlite的数据字段使用实体类的属性名称;
3. sqlite的数据类型统一设为text(引起sqlite这种文本数据库是动态类型的,存储的本质都是文本)
4. 实体类的类型统一都设为NSString
做以上的约定,只是减少使用过程中,由于类型的不同造成不必要的麻烦,如果要支持各种类型,需要编写各种判断代码,进行格式的处理,有兴趣的同学可以进一步研究,呵呵。
下面介绍几个重要的方法。
因为用到了Obj-c运行时的相关方法,所以需要导入运行时的头文件。
#import <objc/runtime.h>
直接扩展了NSObject,名称叫NSObject+Property,文章末尾提供文件下载。
@interface NSObject (Property)
@end
1. 利用反射取得NSObject的属性,并存入到数组中
- (NSArray *)getPropertyList: (Class)clazz{ u_int count; objc_property_t *properties = class_copyPropertyList(clazz, &count); NSMutableArray *propertyArray = [NSMutableArray arrayWithCapacity:count]; for (int i = 0; i < count ; i++) { const char* propertyName = property_getName(properties[i]); [propertyArray addObject: [NSString stringWithUTF8String: propertyName]]; } free(properties); return propertyArray; }
2. 根据属性生成创建Sqlite表的语句
- (NSString *)tableSql:(NSString *)tablename{ NSMutableString *sql = [[NSMutableString alloc] init]; NSArray *array = [self getPropertyList]; [sql appendFormat:@"create table %@ (",tablename] ; NSInteger i = 0; for (NSString *key in array) { if (i>0) { [sql appendString:@","]; } [sql appendFormat:@"%@ text",key]; i++; } [sql appendString:@")"]; return sql; }
3. 把一个实体对象,封装成字典Dictionary
- (NSDictionary *)convertDictionary{ NSMutableDictionary *dict = [[NSMutableDictionary alloc] init]; NSArray *propertyList = [self getPropertyList]; for (NSString *key in propertyList) { SEL selector = NSSelectorFromString(key); #pragma clang diagnostic push #pragma clang diagnostic ignored "-Warc-performSelector-leaks" id value = [self performSelector:selector]; #pragma clang diagnostic pop if (value == nil) { value = [NSNull null]; } [dict setObject:value forKey:key]; } return dict; }
4. 从一个字典中还原成一个实体对象
- (void)dictionaryForObject:(NSDictionary*) dict{ for (NSString *key in [dict allKeys]) { id value = [dict objectForKey:key]; if (value==[NSNull null]) { continue; } if ([value isKindOfClass:[NSDictionary class]]) { id subObj = [self valueForKey:key]; if (subObj) [subObj dictionaryForObject:value]; } else{ [self setValue:value forKeyPath:key]; } } }
5. 返回一个对象的类型名称
- (NSString *)className{ return [NSString stringWithUTF8String:object_getClassName(self)]; }
以上是对NSObject的一个扩展,使用了Obj-C的Category特性
以下是与数据存储相关,定义为DbHelper,使用FMDB库对sqlite进行操作
1. 把id类型的数据对象插入到数据库
-(void)insertObject:(id)object{ NSString *tablename = [object className]; NSMutableString *sql = [[NSMutableString alloc] init]; NSArray *array = [object getPropertyList]; [sql appendFormat:@"insert into %@ (",tablename] ; NSInteger i = 0; for (NSString *key in array) { if (i>0) { [sql appendString:@","]; } [sql appendFormat:@"%@",key]; i++; } [sql appendString:@") values ("]; NSMutableArray *arrayValue = [NSMutableArray array]; i=0; for (NSString *key in array) { SEL selector = NSSelectorFromString(key); #pragma clang diagnostic push #pragma clang diagnostic ignored "-Warc-performSelector-leaks" id value = [object performSelector:selector]; #pragma clang diagnostic pop if (value==nil) { value = @""; } [arrayValue addObject:value]; if (i>0) { [sql appendString:@","]; } [sql appendString:@"?"]; i++; } [sql appendString:@")"]; [_db executeUpdate:sql withArgumentsInArray:arrayValue]; }
2. 把字典NSDictionary对象插入到数据库中
在与服务器进行交互时候,我们一般采用Json进行数据通讯,从服务端获取Json字符,通过JSONKit框架,反序列化成NSDictionary对象,然后插入到数据库
生成插入的sql语句
-(NSString *)createInsertSqlByDictionary:(NSDictionary *)dict tablename:(NSString *)table{ NSMutableString *sql = [[NSMutableString alloc] init]; [sql appendFormat:@"insert into %@ (",table] ; NSInteger i = 0; for (NSString *key in dict.allKeys) { if (i>0) { [sql appendString:@","]; } [sql appendFormat:@"%@",key]; i++; } [sql appendString:@") values ("]; i = 0; for (NSString *key in dict.allKeys) { if (i>0) { [sql appendString:@","]; } [sql appendFormat:@":%@",key]; i++; } [sql appendString:@")"]; return sql; }
把字典插入到数据库中
-(void)insertBySql:(NSString *)sql dict:(NSDictionary *)dict{ if (sql && sql.length>0) { [_dbQueue inDatabase:^(FMDatabase *db) { [db executeUpdate:sql withParameterDictionary:dict];}];}}
取数据
3. 从数据库取数据,封装成成字典,然后放入到数组中
-(NSArray *)queryDbToDictionaryArray:(NSString *)tablename sql:(NSString *)sql{ FMResultSet *resultSet=[_db executeQuery:sql]; NSArray *columnArray = [self fMSetColumnArray:resultSet]; NSMutableArray *syncArray = [[NSMutableArray alloc] init]; NSString *columnName = nil; while ([resultSet next]) { NSMutableDictionary *syncData = [[NSMutableDictionary alloc] init]; for(int i =0;i<columnArray.count;i++) { columnName = [columnArray objectAtIndex:i]; NSString *columnValue = [resultSet stringForColumn: columnName]; if (columnValue==nil) { columnValue=@""; } [syncData setObject:columnValue forKey:columnName]; } [syncArray addObject:syncData]; } if ([syncArray count]==0) { return nil; } return syncArray; }
4. 从数据库中取数据,封装成对象,然后放入数组中-(NSArray *)queryDbToObjectArray:(Class )clazz sql:(NSString *)sql{ FMResultSet *resultSet=[_db executeQuery:sql]; NSArray *columnArray = [self fMSetColumnArray:resultSet]; NSMutableArray *syncArray = [[NSMutableArray alloc] init]; NSString *columnName = nil; while ([resultSet next]) { NSObject *obj = [[clazz alloc] init]; if (obj==nil) { continue; } for(int i =0;i<columnArray.count;i++) { columnName = [columnArray objectAtIndex:i]; NSString *columnValue = [resultSet stringForColumn: columnName]; SEL selector = NSSelectorFromString(columnName); if ([obj respondsToSelector:selector]) { [obj setValue:columnValue forKeyPath:columnName ]; } } [syncArray addObject:obj]; } if ([syncArray count]==0) { return nil; } return syncArray; }
在数据量很大时候,考虑到性能问题,此方法需要酌情使用。
好了,最后提供这四个文件DbHelper.h、DbHelper.m、NSObject+Property.h、NSObject+Property.m的下载。
有了这几个东西,进行开发就省去了很多时间和代码量,直接动态生成表,从服务端接口取到数据,直接插入到数据库中保存,显示数据时,从数据库中取出数据放入到对象数组中。由于项目的保密性,不可能提供整个文件的下载,只提供了关键的几个文件,同学们可以根据需要进行扩展和研究,也欢迎一起探讨,甚至拍砖。
不知道怎么传附件,丢到网站里面了。
http://www.shouji138.com/files/NSObject_DbHelper.zip
补充下如何使用。
1. 插入数据
假设定义如下的实体类
@interface AppInfo : NSObject @property (nonatomic,strong) NSString *AppId; @property (nonatomic,strong) NSString *AppName; @property (nonatomic,strong) NSString *AppIcon; @property (nonatomic,strong) NSString *AppIntro; @end @implementation AppInfo @end
假设从服务端接口中获取到了JSON数据对象
第一步,先将json字符串反序列化成字典NSDictionaryNSDictionary * result = [[operation responseString] objectFromJSONString];
第二部,动态创建表,然后把数据保存进去
DbHelper *dbhelper = [DbHelper instance]; NSString *tablename = @"AppInfo"; [dbhelper DropExistsTable:tablename]; [dbhelper createTableByClass:[AppInfo class]]; NSString *insertSql = nil; //循环插入数据,parmer是json数据里面的一个数组 for (NSDictionary * row in parmer) { insertSql = [dbhelper createInsertSqlByDictionary:row tablename:tablename]; [dbhelper insertBySql:insertSql dict:row]; }
2. 查询数据,放入到数组中
-(NSArray *)getAllApps{ NSString *sql = @"select * from AppInfo " ; NSArray *result = [_dbhelper queryDbToObjectArray:[AppInfo class] sql:sql]; return result; }