前言


 

  最近在GitHub上看了一份關(guān)于基于runtime封裝的對象存儲型數(shù)據(jù)庫的開源代碼,覺得非常值得分享記錄一下,在IOS中對數(shù)據(jù)庫的操作一般通過CoreData和SQLite,CoreData 雖然能夠?qū)C對象轉(zhuǎn)化成數(shù)據(jù),保存在SQLite數(shù)據(jù)庫文件中,也能夠?qū)⒈4嬖跀?shù)據(jù)庫中的數(shù)據(jù)還原成OC對象,期間不需要編寫SQL語句,但使用起來并不是那么方便,而SQLite則需要用戶編寫相應(yīng)的數(shù)據(jù)庫語句,看起來不是很美觀,所以大家一般都會將其進行封裝,讓其使用起來更加方便,而LHDB就是建立在SQLite之上的封裝?,F(xiàn)在,我們來看其是如何實現(xiàn)的,不過在此之前,我先假設(shè)大家對OC的runtime機制和sqlite有一定的理解。附上源碼下載地址:github鏈接

 

實現(xiàn)


 

  所謂的基于對象存儲的數(shù)據(jù)庫,顧名思義,就是一切對數(shù)據(jù)的操作都是對對象模型的操作,通過給對象模型屬性賦值,然后將對象交由底層去解析,轉(zhuǎn)化成相應(yīng)的SQL語句,然后執(zhí)行數(shù)據(jù)庫操作,我們先看看整體的一個LHDB目錄結(jié)構(gòu)(這里僅寫出頭文件):

LHDB

  • LHDBPath.h                //記錄數(shù)據(jù)庫路徑

  • LHModelStateMent.h           //提供一系列將對象模型轉(zhuǎn)化成相應(yīng)的SQL語句的接口

  • LHPredicate.h                    //條件語句處理類

  • LHSqlite.h                         //真正執(zhí)行數(shù)據(jù)庫操作的類

  • NSObject+LHDB.h              //對外提供一系列數(shù)據(jù)庫操作接口

LHModel

  • LHObjectInfo.h                  //聲明了兩個類,LHClassInfo 記錄類信息,LHObjectInfo 記錄類對象屬性的信息(包括屬性Type,Getter和Setter)

  • NSObject+LHModel.h            //提供一系列對象模型信息和數(shù)據(jù)轉(zhuǎn)換相關(guān)的接口

下面我將從我們正常使用數(shù)據(jù)庫的流程去解析LHDB對數(shù)據(jù)庫的管理

  1.  創(chuàng)建數(shù)據(jù)庫表

    先聲明了一個模型類Teacher,如下:

大數(shù)據(jù)培訓(xùn),云培訓(xùn),數(shù)據(jù)挖掘培訓(xùn),云計算培訓(xùn),高端軟件開發(fā)培訓(xùn),項目經(jīng)理培訓(xùn)

 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

大數(shù)據(jù)培訓(xùn),云培訓(xùn),數(shù)據(jù)挖掘培訓(xùn),云計算培訓(xùn),高端軟件開發(fā)培訓(xùn),項目經(jīng)理培訓(xùn)

然后我們調(diào)用聲明在NSObject+LHDB.h中的類方法createTable,

1 [Teacher createTable];

大數(shù)據(jù)培訓(xùn),云培訓(xùn),數(shù)據(jù)挖掘培訓(xùn),云計算培訓(xùn),高端軟件開發(fā)培訓(xùn),項目經(jīng)理培訓(xùn)

 + (      LHSqlite* sqlite =     sqlite.sqlPath =  }

大數(shù)據(jù)培訓(xùn),云培訓(xùn),數(shù)據(jù)挖掘培訓(xùn),云計算培訓(xùn),高端軟件開發(fā)培訓(xùn),項目經(jīng)理培訓(xùn)

大數(shù)據(jù)培訓(xùn),云培訓(xùn),數(shù)據(jù)挖掘培訓(xùn),云計算培訓(xùn),高端軟件開發(fā)培訓(xùn),項目經(jīng)理培訓(xùn)

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

大數(shù)據(jù)培訓(xùn),云培訓(xùn),數(shù)據(jù)挖掘培訓(xùn),云計算培訓(xùn),高端軟件開發(fā)培訓(xùn),項目經(jīng)理培訓(xùn)

這里注意的一點就是在createTable方法中,對數(shù)據(jù)庫路徑的獲取,它主要是在LHDBPath中指定了,如果沒有指定,則使用默認,這意味著,我們可以在外部修改這個數(shù)據(jù)庫路徑名,便可以變更數(shù)據(jù)庫了。

接下來調(diào)用了LHSqlite中 executeUpdateWithSqlstring:parameter: 方法真正對數(shù)據(jù)庫進行相關(guān)對操作,在上面的調(diào)用中,該方法的第一個參數(shù)是一個SQL語句串,而第二個參數(shù)是一個屬性值表,上面調(diào)用了一個全局方法createTableString(self),得到一個創(chuàng)建表的SQL語句,該方法聲明在了LHModelStateMent中,其定義如下:

LHModelStateMent.m

大數(shù)據(jù)培訓(xùn),云培訓(xùn),數(shù)據(jù)挖掘培訓(xùn),云計算培訓(xùn),高端軟件開發(fā)培訓(xùn),項目經(jīng)理培訓(xùn)

 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 }

大數(shù)據(jù)培訓(xùn),云培訓(xùn),數(shù)據(jù)挖掘培訓(xùn),云計算培訓(xùn),高端軟件開發(fā)培訓(xùn),項目經(jīng)理培訓(xùn)

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 "

大數(shù)據(jù)培訓(xùn),云培訓(xùn),數(shù)據(jù)挖掘培訓(xùn),云計算培訓(xùn),高端軟件開發(fā)培訓(xùn),項目經(jīng)理培訓(xùn)

 1 static NSString* tableNameValueString(NSString* type,NSString* name) 2 { 3     //將oc中的type字符串轉(zhuǎn)換成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  //可見其他類型,將被當(dāng)做TEXT類型處理,包括NSDictionary,NSArray22         return [NSString stringWithFormat:@"%@ %@%@",name,@"TEXT",finalStr];23 }

大數(shù)據(jù)培訓(xùn),云培訓(xùn),數(shù)據(jù)挖掘培訓(xùn),云計算培訓(xùn),高端軟件開發(fā)培訓(xùn),項目經(jīng)理培訓(xùn)

 

上面標紅的方法getAllPropertyNameAndType是一個類方法,被聲明在NSObject+LHModel中,返回的是記錄類的所有屬性名和其相應(yīng)類型的字典。

NSObject+LHModel.m

大數(shù)據(jù)培訓(xùn),云培訓(xùn),數(shù)據(jù)挖掘培訓(xùn),云計算培訓(xùn),高端軟件開發(fā)培訓(xùn),項目經(jīng)理培訓(xùn)

 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 }

大數(shù)據(jù)培訓(xùn),云培訓(xùn),數(shù)據(jù)挖掘培訓(xùn),云計算培訓(xùn),高端軟件開發(fā)培訓(xùn),項目經(jīng)理培訓(xùn)

大數(shù)據(jù)培訓(xùn),云培訓(xùn),數(shù)據(jù)挖掘培訓(xùn),云計算培訓(xùn),高端軟件開發(fā)培訓(xùn),項目經(jīng)理培訓(xùn)

 1 static id objectType(NSString* typeString) 2 { 3     //當(dāng)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 }

大數(shù)據(jù)培訓(xùn),云培訓(xùn),數(shù)據(jù)挖掘培訓(xùn),云計算培訓(xùn),高端軟件開發(fā)培訓(xùn),項目經(jīng)理培訓(xùn)

 

 下面終于到了最后一步,就是executeUpdateWithSqlstring:parameter:的調(diào)用,它基本的一個過程就是,先打開數(shù)據(jù)庫,這跟我們之前在LHDBPath中指定的路徑關(guān)聯(lián),指定哪個就打開哪個數(shù)據(jù)庫,然后會從緩存中根據(jù)sqlString,讀取相應(yīng)的sqlite3_stmt結(jié)構(gòu)數(shù)據(jù),如果存在,就reset它,然后重新綁定參數(shù),如果不存在,那就進行轉(zhuǎn)換,然后再保存到緩存中,其具體定義如下:

LHSqlite.m

大數(shù)據(jù)培訓(xùn),云培訓(xùn),數(shù)據(jù)挖掘培訓(xùn),云計算培訓(xùn),高端軟件開發(fā)培訓(xùn),項目經(jīng)理培訓(xùn)

 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(@"打開數(shù)據(jù)庫失敗");16     }17     sqlite3_close(_db);18     UnLock;19 }

大數(shù)據(jù)培訓(xùn),云培訓(xùn),數(shù)據(jù)挖掘培訓(xùn),云計算培訓(xùn),高端軟件開發(fā)培訓(xùn),項目經(jīng)理培訓(xùn)

其中stmtWithCacheKey:返回sqlite3_stmt結(jié)構(gòu)類型指針,

大數(shù)據(jù)培訓(xùn),云培訓(xùn),數(shù)據(jù)挖掘培訓(xùn),云計算培訓(xùn),高端軟件開發(fā)培訓(xùn),項目經(jīng)理培訓(xùn)

 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