(1)知识准备【利用objective-c的runtime特性,结合FMDB实现一个轻量级的ORM】

    版权声明:本文为博主原创文章,未经博主允许不得转载。

(本ORM的源码已经上传到github上 (https://github.com/helloclq/BCSqliteORM_FMDB),大家可以下载测试,如发现什么问题或意见,欢迎大家提出并指正,oschina上的地址为:http://git.oschina.net/BlockCheng/BCSqliteORM_FMDB  )

    想自己写一个objective-c的框架:利用objective-c的runtime特性,结合现有的操作sqlite的FMDB库,实现一个轻量级的ORM框架;要求: 颗粒度小、 量级轻、可配置度高、自由性大;

0、需求缘由

   一个人扛这个项目快两年了,最近在着手重构代码,想精简下现有的数据库层的代码。

   目前项目的数据库层的情况是这个样子的:项目中共涉及的表有8张,数据库的操作,都是通过FMDB操作Sql直接实现的,都是纯粹的objective-c实体和数据库表间的映射操作,如下图。

161236_9CFV_173728.png

项目中,一张表,一个实体,对应一个DBManager类别,而各个类别里面的操作无非都是增删改查,类别里大致操作流程是:

【增删改】:根据objective-c实体信息,手动生成sql,然后经由FMDB,直接操作放进sqlite

【查】:根据查询条件,手动生成sql,利用FMDB操作sqlite,得到查询结果,然后手动映射成objective-c实体

 8个实体对8个表,每个增删改查等都手动拼写一遍,总共8遍,而这些实体对数据库的步骤和代码格式基本类似,可以说,这里存在大量的“冗余”代码,必须优化!

   另外一个就是,每次需求变更或迭代时,每给数据库增加一个字段或减少一个字段,都要在 数据库模块 和 实体 间来回修改,着实浪费时间和精力!

.....(How fucking the code is!此处省略一万字....)

    故必须优化这部分。。。

  不重复发明轮子是软件开发过程中的基本思想和原则,我也试着在github等网站上找过其他的开发项目,但很遗憾,没有找到能满足我需求的开源orm库,我需要的是这样的:

  •  不需要持久化化实体中的每个属性,可以根据自己需要,手动指定存储哪些字段。

  • 数据库和实体间的映射关系可以自己手动指定,不强制绑定。

  • 支持内置指定数据库,app内置数据。

  • 支持各种SQL条件查询、更新、删除。

  • 支持SQL操作日志输出,方便debug。

.....(How fucking the code is!此处有省略....)

  (有人问过,当初项目架构时,为啥选用直接用FMDB操作Sqlite,而不用iOS的coredata,我有一堆的原因,比如直接操作sql高效明了,coredata难用,另外一个最重要的原因是coredata不方便复用,不方便我以后将代码直接翻译到android平台上...总之,一万个理由)

1、sqlite基本知识点

  简单地提下针对本次ORM相关的sqlite知识点。

   sqlite官方站点:http://www.sqlite.org/ 

   SQLite支持哪些数据类型些?

  •   NULL 值为NULL(本次忽略)

  • INTEGER 值为带符号的整型,根据类别用1,2,3,4,6,8字节存储

  • REAL 值为浮点型,8字节存储

  • TEXT 值为text字符串,使用数据库编码(UTF-8, UTF-16BE or UTF-16-LE)存储

  • BLOB 值为二进制数据,具体看实际输入 (本次忽略)

点到即可,其他的可以参考:

http://zetcode.com/db/sqlite/

https://www.google.com/#q=sqlite+tutorial


2、FMDB基本知识点

这里也只是简单的提下本次ORM相关的FMDB相关的知识,具体的请参考其github网页:https://github.com/ccgus/fmdb

创建数据库   

FMDatabase *db = [FMDatabase databaseWithPath:@"/tmp/tmp.db"];

打开数据库

if (![db open]) {
    [db release];
    return;
}

执行查找

FMResultSet *s = [db executeQuery:@"SELECT * FROM myTable"];
while ([s next]) {
    //retrieve values for each record
}
FMResultSet *s = [db executeQuery:@"SELECT COUNT(*) FROM myTable"];
if ([s next]) {
    int totalCount = [s intForColumnIndex:0];
}

从FMDB的查询结果中去对应的objective-c的数据方式有:

intForColumn:
longForColumn:
longLongIntForColumn:
boolForColumn:
doubleForColumn:
stringForColumn:
dateForColumn:
dataForColumn:
dataNoCopyForColumn:
UTF8StringForColumnName:
objectForColumnName:

关闭数据库

[db close];

事物相关(本次忽略)

NSString *sql = @"create table bulktest1 (id integer primary key autoincrement, x text);"
                 "create table bulktest2 (id integer primary key autoincrement, y text);"
                 "create table bulktest3 (id integer primary key autoincrement, z text);"
                 "insert into bulktest1 (x) values ('XXX');"
                 "insert into bulktest2 (y) values ('YYY');"
                 "insert into bulktest3 (z) values ('ZZZ');";

success = [db executeStatements:sql];

sql = @"select count(*) as count from bulktest1;"
       "select count(*) as count from bulktest2;"
       "select count(*) as count from bulktest3;";

success = [self.db executeStatements:sql withResultBlock:^int(NSDictionary *dictionary) {
    NSInteger count = [dictionary[@"count"] integerValue];
    XCTAssertEqual(count, 1, @"expected one record for dictionary %@", dictionary);
    return 0;
}];

常用数据插入

NSDictionary *argsDict = [NSDictionary dictionaryWithObjectsAndKeys:@"My Name", @"name", nil];
[db executeUpdate:@"INSERT INTO myTable (name) VALUES (:name)" withParameterDictionary:argsDict];
[db executeUpdate:@"INSERT INTO myTable VALUES (?)", @"this has \" lots of ' bizarre \" quotes '"];
[db executeUpdate:@"INSERT INTO myTable VALUES (?)", 42];
[db executeUpdateWithFormat:@"INSERT INTO myTable VALUES (%d)", 42];

 更常用的使用FMDatabaseQueue来安全的操作

FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithPath:aPath];
[queue inDatabase:^(FMDatabase *db) {
    [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:1]];
    [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:2]];
    [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:3]];

    FMResultSet *rs = [db executeQuery:@"select * from foo"];
    while ([rs next]) {
        …
    }
}];
 
[queue inTransaction:^(FMDatabase *db, BOOL *rollback) {
    [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:1]];
    [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:2]];
    [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:3]];

    if (whoopsSomethingWrongHappened) {
        *rollback = YES;
        return;
    }
    // etc…
    [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:4]];
}];


3、基本objective-c的runtime特性

介绍objective-c runtime的文章很多,这里,我只说我本次用到的几条。

获取一个类的类名

NSString *NSStringFromClass(Class aClass);

根据属性名获取一个类的属性

objc_property_t   class_getProperty(Class cls, const char *name)

根据属性获取属性名字

const char *property_getName(objc_property_t property)

char* 为UTF-8类型,转为NSString的方式为

+ (NSString*)stringWithUTF8String:(const char *)nullTerminatedCString;

综合上述

 NSString* tmpPropertyName  = obj;
 objc_property_t tmpPropery = class_getProperty(entityClass, [tmpPropertyName UTF8String]);
 const char * type = property_getAttributes(tmpPropery);
NSString * typeString = [NSString stringWithUTF8String:type];

根据一个属性名字和一个对象,获取其对应的值

NSString* propertyName = key;
NSOject *entity;
id propertyValue = [(NSObject*)entity valueForKey:propertyName];

根据一个属性名字,判断一个属性的数据类型

 NSString* tmpPropertyName  = obj;
 objc_property_t tmpPropery = class_getProperty(entityClass, [tmpPropertyName UTF8String]);
const char * type = property_getAttributes(tmpPropery);
NSString * typeString = [NSString stringWithUTF8String:type];
NSArray * attributes = [typeString componentsSeparatedByString:@","];
NSString * typeAttribute = [attributes objectAtIndex:0];
NSString * propertyType = [typeAttribute substringFromIndex:1];
const char * rawPropertyType = [propertyType UTF8String];
//https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html
if (strcmp(rawPropertyType, @encode(CGFloat)) == 0 ||strcmp(rawPropertyType, @encode(float)) == 0 ||strcmp(rawPropertyType, @encode(double)) == 0 )/*0001*/ {
    //it's a float    
 } else if (strcmp(rawPropertyType, @encode(int)) == 0) { 
    //it's an int 
 } else if (strcmp(rawPropertyType, @encode(long)) == 0) {  
   //it's a long   
} else if (strcmp(rawPropertyType, @encode(long long)) == 0) {  
  //it's   long long 
} else if (strcmp(rawPropertyType, @encode(id)) == 0) {   
 //it's some sort of object: id    
 }  else if (strcmp(rawPropertyType, @encode(BOOL)) == 0 || strcmp(rawPropertyType, @encode(_Bool)) == 0) { 
    //it's some sort of object: id   
} else {   
 // According to Apples Documentation you can determine the corresponding encoding values
 
}/*0001*/

if ([typeAttribute hasPrefix:@"T@"] && [typeAttribute length] > 1)/*0001*/ {  
  NSString * typeClassName = [typeAttribute substringWithRange:NSMakeRange(3, [typeAttribute length]-4)];  //turns @"NSDate" into NSDate   
  Class typeClass = NSClassFromString(typeClassName);  
  if (typeClass != nil) {    
      if (typeClass == [NSDate class]) {  
            //nsdate                 
     }else if (typeClass == [NSString class] ||typeClass == [NSMutableString class] ) {         
     }                       
 }
 
}/*0001*/

4、其他平台常见的ORM实现原理简述

同上,这里也只是简述。

java web中,我用过的ORM 框架有,hibernate、mybatis,而他们的实现基本原理可以简述为:反射 + XML或annotation配置 + 基本设计模式,生成sql来操作数据库。

android平台上,好些同学写了orm和其他的一些注入框架,用的也基本都是 【反射+注解+基本模式】。


5、实现基本思路

鉴于4中的简述,故本次orm中,我也参照这个模式进行构建,java中的反射,对应objective-c中,就是runtime,但遗憾的是,objective-c中无注解,或者也没啥人用xml,plist配置也不现实。故我只能采用绕道的方式实现了:协议 + 类方法。

故本次ORM的基本原理可以概括为:

【runtime】 + 【协议 + 类方法】 + 基本模式 + FMDB 。


-------------------------------本次完,分割线--------------------------------------

(2)预期+思考【利用objective-c的runtime特性,结合FMDB实现轻量级的ORM】


(3)实体和结构【利用objective-c的runtime特性,结合FMDB实现轻量级的ORM】

(4)代码及测试【利用objective-c的runtime特性,结合FMDB实现轻量级的ORM】


【后续持续更新】


再次声明:本文为博主原创文章,未经博主允许不得转载。

(以前在本站及一些其他论坛中发表过几篇博客,后被某些知名网站博主直接copy后,改为自己的原创,碍于时间成本,懒得追究,故本问两次强调,请自重!!)

转载于:https://my.oschina.net/chengliqun/blog/504020

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值