(1)知識準備【利用objective-c的runtime特性,結合FMDB實現一個輕量級的ORM】

    版權聲明:本文爲博主原創文章,未經博主容許不得轉載。html

(本ORM的源碼已經上傳到github上 (https://github.com/helloclq/BCSqliteORM_FMDB),你們能夠下載測試,如發現什麼問題或意見,歡迎你們提出並指正,oschina上的地址爲:http://git.oschina.net/BlockCheng/BCSqliteORM_FMDB  )java

    想本身寫一個objective-c的框架:利用objective-c的runtime特性,結合現有的操做sqlite的FMDB庫,實現一個輕量級的ORM框架;要求: 顆粒度小、 量級輕、可配置度高、自由性大;android

0、需求原因

   一我的扛這個項目快兩年了,最近在着手重構代碼,想精簡下現有的數據庫層的代碼。ios

   目前項目的數據庫層的狀況是這個樣子的:項目中共涉及的表有8張,數據庫的操做,都是經過FMDB操做Sql直接實現的,都是純粹的objective-c實體和數據庫表間的映射操做,以下圖。git

項目中,一張表,一個實體,對應一個DBManager類別,而各個類別裏面的操做無非都是增刪改查,類別裏大體操做流程是:github

【增刪改】:根據objective-c實體信息,手動生成sql,而後經由FMDB,直接操做放進sqliteweb

【查】:根據查詢條件,手動生成sql,利用FMDB操做sqlite,獲得查詢結果,而後手動映射成objective-c實體objective-c

 8個實體對8個表,每一個增刪改查等都手動拼寫一遍,總共8遍,而這些實體對數據庫的步驟和代碼格式基本相似,能夠說,這裏存在大量的「冗餘」代碼,必須優化!sql

   另一個就是,每次需求變動或迭代時,每給數據庫增長一個字段或減小一個字段,都要在 數據庫模塊 和 實體 間來回修改,着實浪費時間和精力!
數據庫

.....(How fucking the code is!此處省略一萬字....)

    故必須優化這部分。。。

  不重複發明輪子是軟件開發過程當中的基本思想和原則,我也試着在github等網站上找過其餘的開發項目,但很遺憾,沒有找到能知足我需求的開源orm庫,我須要的是這樣的:

  •  不須要持久化化實體中的每一個屬性,能夠根據本身須要,手動指定存儲哪些字段。

  • 數據庫和實體間的映射關係能夠本身手動指定,不強制綁定。

  • 支持內置指定數據庫,app內置數據。

  • 支持各類SQL條件查詢、更新、刪除。

  • 支持SQL操做日誌輸出,方便debug。

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

  (有人問過,當初項目架構時,爲啥選用直接用FMDB操做Sqlite,而不用iOS的coredata,我有一堆的緣由,好比直接操做sql高效明瞭,coredata難用,另一個最重要的緣由是coredata不方便複用,不方便我之後將代碼直接翻譯到android平臺上...總之,一萬個理由)

一、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


二、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]];
}];


三、基本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*/

四、其餘平臺常見的ORM實現原理簡述

同上,這裏也只是簡述。

java web中,我用過的ORM 框架有,hibernate、mybatis,而他們的實現基本原理能夠簡述爲:反射 + XML或annotation配置 + 基本設計模式,生成sql來操做數據庫。

android平臺上,好些同窗寫了orm和其餘的一些注入框架,用的也基本都是 【反射+註解+基本模式】。


五、實現基本思路

鑑於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後,改成本身的原創,礙於時間成本,懶得追究,故本問兩次強調,請自重!!)

相關文章
相關標籤/搜索