前言


 

  最近在GitHub上看了一份关于基于runtime封装的对象存储型数据库的开源代码,觉得非常值得分享记录一下,在IOS中对数据库的操作一般通过CoreData和SQLite,CoreData 虽然能够将OC对象转化成数据,保存在SQLite数据库文件中,也能够将保存在数据库中的数据还原成OC对象,期间不需要编写SQL语句,但使用起来并不是那么方便,而SQLite则需要用户编写相应的数据库语句,看起来不是很美观,所以大家一般都会将其进行封装,让其使用起来更加方便,而LHDB就是建立在SQLite之上的封装。现在,我们来看其是如何实现的,不过在此之前,我先假设大家对OC的runtime机制和sqlite有一定的理解。附上源码下载地址:github链接

 

实现


 

  所谓的基于对象存储的数据库,顾名思义,就是一切对数据的操作都是对对象模型的操作,通过给对象模型属性赋值,然后将对象交由底层去解析,转化成相应的SQL语句,然后执行数据库操作,我们先看看整体的一个LHDB目录结构(这里仅写出头文件):

LHDB

  • LHDBPath.h                //记录数据库路径

  • LHModelStateMent.h           //提供一系列将对象模型转化成相应的SQL语句的接口

  • LHPredicate.h                    //条件语句处理类

  • LHSqlite.h                         //真正执行数据库操作的类

  • NSObject+LHDB.h              //对外提供一系列数据库操作接口

LHModel

  • LHObjectInfo.h                  //声明了两个类,LHClassInfo 记录类信息,LHObjectInfo 记录类对象属性的信息(包括属性Type,Getter和Setter)

  • NSObject+LHModel.h            //提供一系列对象模型信息和数据转换相关的接口

下面我将从我们正常使用数据库的流程去解析LHDB对数据库的管理

  1.  创建数据库表

    先声明了一个模型类Teacher,如下:

大数据培训,云培训,数据挖掘培训,云计算培训,高端软件开发培训,项目经理培训

 1 @interface Teacher : NSObject 2  3 @property (nonatomic,strong) NSString* name; 4  5 @property (nonatomic,assign) NSInteger age; 6  7 @property (nonatomic,strong) NSDate* updateDate; 8  9 @property (nonatomic,strong) NSData* data;10 11 @end

大数据培训,云培训,数据挖掘培训,云计算培训,高端软件开发培训,项目经理培训

然后我们调用声明在NSObject+LHDB.h中的类方法createTable,

1 [Teacher createTable];

大数据培训,云培训,数据挖掘培训,云计算培训,高端软件开发培训,项目经理培训

 + (      LHSqlite* sqlite =     sqlite.sqlPath =  }

大数据培训,云培训,数据挖掘培训,云计算培训,高端软件开发培训,项目经理培训

大数据培训,云培训,数据挖掘培训,云计算培训,高端软件开发培训,项目经理培训

 + (NSString*       ([LHDBPath instanceManagerWith:nil].dbPath.length ==               }          }

大数据培训,云培训,数据挖掘培训,云计算培训,高端软件开发培训,项目经理培训

这里注意的一点就是在createTable方法中,对数据库路径的获取,它主要是在LHDBPath中指定了,如果没有指定,则使用默认,这意味着,我们可以在外部修改这个数据库路径名,便可以变更数据库了。

接下来调用了LHSqlite中 executeUpdateWithSqlstring:parameter: 方法真正对数据库进行相关对操作,在上面的调用中,该方法的第一个参数是一个SQL语句串,而第二个参数是一个属性值表,上面调用了一个全局方法createTableString(self),得到一个创建表的SQL语句,该方法声明在了LHModelStateMent中,其定义如下:

LHModelStateMent.m

大数据培训,云培训,数据挖掘培训,云计算培训,高端软件开发培训,项目经理培训

 1 NSString* createTableString(Class modelClass) 2 { 3     NSMutableString* sqlString = [NSMutableString stringWithString:CREATE_TABLENAME_HEADER]; 4     NSDictionary* stateMentDic = [modelClass getAllPropertyNameAndType];  //key为属性名,value为属性类型字符串,如:NSString,i,Q,d等等 5     [sqlString appendString:NSStringFromClass(modelClass)]; //类名做为表名 6     NSMutableString* valueStr = [NSMutableString string]; 7     [stateMentDic enumerateKeysAndObjectsUsingBlock:^(NSString* key, NSString* obj, BOOL* stop) { 8         obj = [NSString stringWithFormat:@"%@",obj]; 9         [valueStr appendString:tableNameValueString(obj, key)];10     }];11     if (valueStr.length>0) {12         [valueStr deleteCharactersInRange:NSMakeRange(valueStr.length-1, 1)];13     }14     [sqlString appendFormat:@"(%@)",valueStr];15     return sqlString;16 }

大数据培训,云培训,数据挖掘培训,云计算培训,高端软件开发培训,项目经理培训

1 #define CREATE_TABLENAME_HEADER @"CREATE TABLE IF NOT EXISTS "2 #define INSERT_HEADER @"INSERT INTO "3 #define UPDATE_HEADER @"UPDATE "4 #define DELETE_HEADER @"DELETE FROM "5 #define SELECT_HEADER @"SELECT * FROM "

大数据培训,云培训,数据挖掘培训,云计算培训,高端软件开发培训,项目经理培训

 1 static NSString* tableNameValueString(NSString* type,NSString* name) 2 { 3     //将oc中的type字符串转换成sqlite能认识的类型type,组合成一个字符串,类似@"age INT,"返回,注意后面的","号 4      5     NSString* finalStr = @","; 6     NSString* typeStr = (NSString*)type; 7     if ([typeStr isEqualToString:@"i"]) { 8         return [NSString stringWithFormat:@"%@ %@%@",name,@"INT",finalStr]; 9     }else if ([typeStr isEqualToString:@"f"]) {10         return [NSString stringWithFormat:@"%@ %@%@",name,@"FLOAT",finalStr];11     }else if ([typeStr isEqualToString:@"B"]) {12         return [NSString stringWithFormat:@"%@ %@%@",name,@"BOOL",finalStr];13     }else if ([typeStr isEqualToString:@"d"]) {14         return [NSString stringWithFormat:@"%@ %@%@",name,@"DOUBLE",finalStr];15     }else if ([typeStr isEqualToString:@"q"]) {16         return [NSString stringWithFormat:@"%@ %@%@",name,@"LONG",finalStr];17     }else if ([typeStr isEqualToString:@"NSData"]||[typeStr isEqualToString:@"UIImage"]) {18         return [NSString stringWithFormat:@"%@ %@%@",name,@"BLOB",finalStr];19     }else if ([typeStr isEqualToString:@"NSNumber"]){20         return [NSString stringWithFormat:@"%@ %@%@",name,@"INT",finalStr];21     } else  //可见其他类型,将被当做TEXT类型处理,包括NSDictionary,NSArray22         return [NSString stringWithFormat:@"%@ %@%@",name,@"TEXT",finalStr];23 }

大数据培训,云培训,数据挖掘培训,云计算培训,高端软件开发培训,项目经理培训

 

上面标红的方法getAllPropertyNameAndType是一个类方法,被声明在NSObject+LHModel中,返回的是记录类的所有属性名和其相应类型的字典

NSObject+LHModel.m

大数据培训,云培训,数据挖掘培训,云计算培训,高端软件开发培训,项目经理培训

 1 + (NSDictionary*)getAllPropertyNameAndType 2 { 3     NSMutableDictionary* dic = [NSMutableDictionary dictionary]; 4     unsigned int count = 0; 5     objc_property_t* property_t = class_copyPropertyList(self, &count); 6     for (int i=0; i<count; i++) { 7         objc_property_t propert = property_t[i]; 8         NSString* propertyName = [NSString stringWithUTF8String:property_getName(propert)]; 9         NSString* propertyType = [NSString stringWithUTF8String:property_getAttributes(propert)];10         [dic setValue:objectType(propertyType) forKey:propertyName];11     }12     free(property_t);13     return dic;14 }

大数据培训,云培训,数据挖掘培训,云计算培训,高端软件开发培训,项目经理培训

大数据培训,云培训,数据挖掘培训,云计算培训,高端软件开发培训,项目经理培训

 1 static id objectType(NSString* typeString) 2 { 3     //当typeString表示是一个oc对象类型的时候,它看起来类似这样:@"T@\"NSString\",&,N,V_name" 4     //否则,它看起来类似这样:@"Ti,N,V_age" 5     if ([typeString containsString:@"@"]) { //type为oc对象时,typeString值类似 @"@\"NSString\"",这时候,分割之后返回的strArray[0]是 @"T@",strArray[1]就是@"NSString"了 6         NSArray* strArray = [typeString componentsSeparatedByString:@"\""]; 7         if (strArray.count >= 1) { 8             return strArray[1]; 9         }else10             return nil;11     }else12         return [typeString substringWithRange:NSMakeRange(1, 1)];13 }

大数据培训,云培训,数据挖掘培训,云计算培训,高端软件开发培训,项目经理培训

 

 下面终于到了最后一步,就是executeUpdateWithSqlstring:parameter:的调用,它基本的一个过程就是,先打开数据库,这跟我们之前在LHDBPath中指定的路径关联,指定哪个就打开哪个数据库,然后会从缓存中根据sqlString,读取相应的sqlite3_stmt结构数据,如果存在,就reset它,然后重新绑定参数,如果不存在,那就进行转换,然后再保存到缓存中,其具体定义如下:

LHSqlite.m

大数据培训,云培训,数据挖掘培训,云计算培训,高端软件开发培训,项目经理培训

 1 - (void)executeUpdateWithSqlstring:(NSString *)sqlString parameter:(NSDictionary*)parameter 2 { 3     Lock; 4     if ([self openDB]) { 5         sqlite3_stmt* stmt = [self stmtWithCacheKey:sqlString]; 6         if (stmt) { 7             for (int i=0; i<parameter.allKeys.count; i++) { 8                 [self bindObject:parameter[parameter.allKeys[i]] toColumn:i+1 inStatement:stmt]; 9             }10             if (sqlite3_step(stmt) != SQLITE_DONE) {11                 LHSqliteLog(@"error = %@",errorForDataBase(sqlString, _db));12             }13         }14     }else {15         LHSqliteLog(@"打开数据库失败");16     }17     sqlite3_close(_db);18     UnLock;19 }

大数据培训,云培训,数据挖掘培训,云计算培训,高端软件开发培训,项目经理培训

其中stmtWithCacheKey:返回sqlite3_stmt结构类型指针,

大数据培训,云培训,数据挖掘培训,云计算培训,高端软件开发培训,项目经理培训

 1 - (sqlite3_stmt*)stmtWithCacheKey:(NSString*)sqlString 2 { 3     sqlite3_stmt* stmt = (sqlite3_stmt*)CFDictionaryGetValue(_stmtCache, (__bridge const void *)([[self.sqlPath lastPathComponent] stringByAppendingString:sqlString])); 4     if (stmt == 0x00) { 5         if (sqlite3_prepare_v2(_db, sqlString.UTF8String, -1, &stmt, nil) == SQLITE_OK) { 6             //缓存stmt 7             CFDictionarySetValue(_stmtCache, (__b

http://www.cnblogs.com/ginvar/p/7226464.html