【Qboy】原创 2012年5月13日
我们知道,objc中的反射机制是很强大的,现在很多语言都已经支持反射机制,Java、.net等,。在公司利用.net的架构中就是采用.net反射机制来实现三层架构,感觉使用起来挺方便,所以就想在ObjC也有反射机制,能不能利用这个帮我们也做些事情呢?刚好在一个项目中要用到数据库,所以想到采用ObjC的反射机制,做一个在ObjC中的简单三层架构。
我们采用的数据库是sqlite3,这是在Iphone/Ipad开发的数据库。
首先我们写好一个Sqlite3Helper类,作为操作数据库的操作类:
+(void)executeNonQuery:(NSString*)sql{ |
sqlite3 *db; |
NSString*dbPath; |
dbPath =[self getDBFilePahth]; |
int openResult=sqlite3_open([dbPath UTF8String],&db); |
char*error; |
if(openResult==SQLITE_OK){ |
NSLog(@"Open Db Success"); |
if(sqlite3_exec(db,[sql UTF8String], NULL, NULL,&error )!= SQLITE_OK) |
{ |
NSAssert(0,@"Error: Can't Execute by Sql = %@ ", sql); |
} |
} |
else{ |
NSAssert(0,@"Error:Can't Open DBFile, DbFilePath =%@ ",dbPath); |
} |
sqlite3_close(db); |
} |
+(sqlite3_stmt *)execQuery:(NSString*)sql{ |
sqlite3 *db; |
NSString*dbPath; |
dbPath =[self getDBFilePahth]; |
int openResult=sqlite3_open([dbPath UTF8String],&db); |
sqlite3_stmt *statement = nil; |
if(openResult==SQLITE_OK){ |
NSLog(@"Open Db Success"); |
if(sqlite3_prepare_v2(db,[sql UTF8String],-1,&statement, NULL)!= SQLITE_OK) |
{ |
NSAssert(0,@"Error: Can't Execute by Sql = %@ ", sql); |
} |
} |
else{ |
NSAssert(0,@"Error:Can't Open DBFile, DbFilePath =%@ ",dbPath); |
} |
sqlite3_close(db); |
return statement; |
} |
+(void)executeNonQuery:(NSString*)sql{ |
sqlite3 *db; |
NSString*dbPath; |
dbPath =[self getDBFilePahth]; |
int openResult=sqlite3_open([dbPath UTF8String],&db); |
char*error; |
if(openResult==SQLITE_OK){ |
NSLog(@"Open Db Success"); |
if(sqlite3_exec(db,[sql UTF8String], NULL, NULL,&error )!= SQLITE_OK) |
{ |
NSAssert(0,@"Error: Can't Execute by Sql = %@ ", sql); |
} |
} |
else{ |
NSAssert(0,@"Error:Can't Open DBFile, DbFilePath =%@ ",dbPath); |
} |
sqlite3_close(db); |
} |
+(sqlite3_stmt *)execQuery:(NSString*)sql{ |
sqlite3 *db; |
NSString*dbPath; |
dbPath =[self getDBFilePahth]; |
int openResult=sqlite3_open([dbPath UTF8String],&db); |
sqlite3_stmt *statement = nil; |
if(openResult==SQLITE_OK){ |
NSLog(@"Open Db Success"); |
if(sqlite3_prepare_v2(db,[sql UTF8String],-1,&statement, NULL)!= SQLITE_OK) |
{ |
NSAssert(0,@"Error: Can't Execute by Sql = %@ ", sql); |
} |
} |
else{ |
NSAssert(0,@"Error:Can't Open DBFile, DbFilePath =%@ ",dbPath); |
} |
sqlite3_close(db); |
return statement; |
} |
主要有两个方法一个是+(void)executeNonQuery:(NSString*)sql用于操作数据库insert,update等,执行类语句。另外一个是+(sqlite3_stmt *)execQuery:(NSString*)sql,用于执行查询类语句,并且返回一个查询结果。
创建一个BaseModel,作为所有Model的基类。目前在BaseModel中没有任何的方法和属性。
创建一个BaseService方法AddModel如下:
-(void)addModel:(BaseModel*)model{ |
unsignedint numofvars =0; |
NSString*sql; |
NSString*vals=@""; |
NSString*values=@""; |
Ivar* ivars=class_copyIvarList([model class],&numofvars); |
int i=0; |
for(constIvar* p = ivars; p< ivars+numofvars;p++){ |
Ivarconst ivar=*p; |
constchar* keytype=ivar_getTypeEncoding(ivar); |
NSString* keyTypeValue =[[NSString stringWithUTF8String:keytype] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; |
NSLog(@"%i:%@",(++i),keyTypeValue); |
NSString* key =[NSString stringWithUTF8String:ivar_getName(ivar)]; |
NSLog(@"val = %@",key); |
if([model valueForKey:key]!=nil){ |
vals=[NSString stringWithFormat:@"%@,%@",vals,key]; |
values=[NSString stringWithFormat:@"%@,\'%@\'",values,[model valueForKey:key]]; |
} |
} |
if(![values isEqualToString:@""]){ |
values =[values substringFromIndex:1]; |
} |
if(![vals isEqualToString:@""]){ |
vals=[vals substringFromIndex:1]; |
} |
sql=[NSString stringWithFormat:@"insert into %@ (%@) values (%@)",tablename_,vals,values]; |
NSLog(@"sql = %@ ",sql); |
[Sqlite3Helper executeNonQuery:sql]; |
NSLog(@"Add Success"); |
} |
在查询中主要有以下几个函数,其中-(id)convertRowToModel:(Class)cls stmt:(sqlite3_stmt *)stmt 是把查出来的一行记录转换为Model的一个实例。-(id)getModel:(NSString*)pkid returnClass:(Class)cls则是根据主键查询某个值。-(NSMutableArray*)getCollection:(NSString*)strWhere orderby:(NSString*)orderby cls:(Class)cls则是根据查询条件排序字段等返回一个Array。
-(id)convertRowToModel:(Class)cls stmt:(sqlite3_stmt *)stmt { |
id model=nil; |
model =[[cls alloc] init]; |
int coloumncount=sqlite3_column_count(stmt); |
for(int i=0; i<coloumncount; i++){ |
char* colname=(char*)sqlite3_column_name(stmt, i); |
NSString* strcolname=[NSString stringWithUTF8String:colname]; |
char* deltyp =(char*)sqlite3_column_decltype(stmt, i); |
NSLog(@"chartype = %s",deltyp); |
int ctype=sqlite3_column_type(stmt, i); |
char* cvalue=(char*)sqlite3_column_text(stmt, i); |
if(cvalue!=NULL){ |
[model setValue:[NSString stringWithUTF8String:cvalue] forKey:strcolname]; |
} |
} |
return model; |
} |
-(id)getModel:(NSString*)pkid returnClass:(Class)cls{ |
NSString*sql=[NSString stringWithFormat:@"select * from %@ where %@ = '%@'",tablename_,tablePK_,pkid]; |
NSLog(@"GetModel Sql =%@",sql); |
sqlite3_stmt* stmt =[Sqlite3Helper execQuery:sql]; |
id model=nil; |
if(stmt!=nil){ |
if(sqlite3_step(stmt)==SQLITE_ROW){ |
model =[self convertRowToModel:cls stmt:stmt]; |
} |
sqlite3_finalize(stmt); |
} |
return model; |
} |
-(NSMutableArray*)getCollection:(NSString*)strWhere orderby:(NSString*)orderby cls:(Class)cls { |
NSString*sql=[NSString stringWithFormat:@"select * from %@ where 1=1 ",tablename_]; |
if(strWhere!=nil&&![strWhere isEqualToString:@""]){ |
sql=[sql stringByAppendingFormat:@" and %@",strWhere]; |
} |
if(orderby!=nil&&![orderby isEqualToString:@""]){ |
sql =[sql stringByAppendingFormat:@" order by %@ ",orderby]; |
} |
NSLog(@"GetModel Sql =%@",sql); |
sqlite3_stmt* stmt =[Sqlite3Helper execQuery:sql]; |
id model=nil; |
NSMutableArray* arr=[NSMutableArray array]; |
if(stmt!=nil){ |
while(sqlite3_step(stmt)==SQLITE_ROW){ |
model =[self convertRowToModel:cls stmt:stmt]; |
[arr addObject:model]; |
} |
sqlite3_finalize(stmt); |
} |
return arr; |
} |
这样,我们就把BaseService的方法实现了,还差一个Update没有实现,我想根据这个也应该很简单。由于在于实现过程需要知道表名和主键,所以在BaseService中定义了两个变量 tablename_和tablePK_。BaseService.h定义如下,特别要主要添加了objc.h和runtime.h。
#import <Foundation/Foundation.h> |
#import "BaseModel.h" |
#import <objc/objc.h> |
#import <objc/runtime.h> |
#import "Sqlite3Helper.h" |
@interfaceBaseService:NSObject |
{ |
NSString* tablename_; |
NSString* tablePK_; |
} |
-(void)addModel:(BaseModel*)model; |
-(void)updateModel:(BaseModel*)model; |
-(bool)deleteModel:(NSString*)pkid; |
-(bool)deleteAll; |
//-(BaseModel*)getModel:(NSString *)pkid returnModel:(BaseModel*)model; |
-(id)getModel:(NSString*)pkid returnClass:(Class)cls; |
-(NSMutableArray*)getCollection:(NSString*)strWhere returnClass:(Class)cls; |
-(NSMutableArray*)getCollection:(NSString*)strWhere orderby:(NSString*)orderby cls:(Class)cls; |
-(BOOL)isExist:(NSString*)pkid; |
-(NSMutableArray*)getCollection:(NSString*)strWhere; |
-(NSMutableArray*)getAll; |
-(id)getModel:(NSString*)pkid; |
-(int)getCountBystrwhere:(NSString*)strwhere; |
@end |
在实现中BaseModel的子类只要实现好与数据库表中各个字段对应好关系例如其中一个BaseModel的子类如下:
@interfaceTicketResult:BaseModel |
{ |
NSString* sectionID; |
NSString* result; |
NSNumber* type; |
} |
@property(nonatomic,retain)NSString*sectionID; |
@property(nonatomic,retain)NSString*result; |
@property(nonatomic,retain)NSNumber*type; |
@end |
其数据库表为
create table T_TicketResult(
sectionID varchar(24) primary key,
result varchar(32) not null,
int type
)
把int 转换成NSNumber主要是为了判断有没有给type赋值,或者如果转换成int类型则判断,因为int的初始值为0。
里面有很多很好的方法,这里就没有使用到了。