zoukankan      html  css  js  c++  java
  • 利用objc中的反射机制拼接Sql语句

          【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。

    反射机制的最权威资料尽在http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/ObjCRuntimeRef/Reference/reference.html#//apple_ref/c/func/class_getClassVariable

    里面有很多很好的方法,这里就没有使用到了。

  • 相关阅读:
    作为一个新手程序员,该如何去挽救一个失败的项目?
    IOS查看APP的crash Log
    UITableView 性能优化(卡问题自检)
    ARC学习笔记(一)
    iPhone的UDID与push中使用的device token的关系
    跳转appstore的评分页面和软件的首页
    IOS项目Jenkins集成脚本举例
    jenkins集成学习心得
    学习设计模式心得
    网页跳转到APP
  • 原文地址:https://www.cnblogs.com/qboy/p/2498514.html
Copyright © 2011-2022 走看看