用一個類根據(jù)Model屬性生成SQL語句

想到寫這個的原因是我和我的一個朋友在寫一個開源項(xiàng)目的時候,我負(fù)責(zé)了數(shù)據(jù)庫的持久化存儲部分,結(jié)果他看到了我的數(shù)據(jù)庫數(shù)據(jù)存儲結(jié)構(gòu)之后直接傻眼了,因?yàn)閭€人比較簡單粗暴,直接把一個數(shù)據(jù)Model歸檔成為二進(jìn)制Data之后丟到了數(shù)據(jù)庫,然后取出來也可以偷偷懶,解檔就是一個模型Model啦,但是我的小伙伴不樂意了,把我批評了一頓,說我吧數(shù)據(jù)庫的“檢索”價值都給寫沒了,好吧,我承認(rèn)當(dāng)時我聽得一臉懵逼,不過好像挺有道理??,不過按照他說的檢索價值,我想的話應(yīng)該就是把數(shù)據(jù)存儲的更為清晰吧,那就要分更多的字段了【長長的SQL語句讓我在角落瑟瑟發(fā)抖??】,唉,個人真的非常懶,想到這些就頭疼,那么我們能不能用一個類根據(jù)Model屬性生成SQL語句呢?答案是肯定的,接著往下看吧!

SQLite背景:【SQL(Structured Query Language)結(jié)構(gòu)語言】SQLite是目前主流的嵌入式關(guān)系型數(shù)據(jù)庫,其主要的特點(diǎn)的就是輕量級,跨平臺,當(dāng)前很多嵌入式操作系統(tǒng)都將其作為數(shù)據(jù)庫的首先。它是基于C語言開發(fā)的數(shù)據(jù)庫!所以我們需要通過hi用c語言語法進(jìn)行數(shù)據(jù)庫的操作和訪問!

思路:SQL語句首先是建表,建表包含了字段名,表名,字段對應(yīng)的存儲類型,那么我們就要先來熟悉一下我們的七種基本數(shù)據(jù)類型以及那些常量類型到了數(shù)據(jù)庫中是什么類型吧,這樣才能和每一個字段對應(yīng)上,既能夠達(dá)到存儲目的,又能節(jié)省空間

SQL數(shù)據(jù)庫中的存儲類型【比較多,但是請耐心看完,并不復(fù)雜SQlite數(shù)據(jù)庫存儲類型

思路演變:我們既然想要用一個類來根據(jù)Model屬性生成SQL,那么這個類肯定應(yīng)該是普遍適用的,否則就沒有多大意義了,那么我們肯定就要獲取這個Model所有的屬性以及屬性對應(yīng)的類型,然后根據(jù)這些屬性名字來生成數(shù)據(jù)庫中對應(yīng)的字段,屬性的類型經(jīng)過處理之后變成數(shù)據(jù)庫中的存儲類型,很明顯,我們又要用運(yùn)行時了!

在了解了存儲類型之后我們就要來真正的動刀了,首先我們要通過運(yùn)行時來解析我們通常所用的數(shù)據(jù)類型在運(yùn)行時中是怎么表示的;這里我就不多說了,直接貼方法,怎么根據(jù)運(yùn)行時獲取一個類所對應(yīng)的屬性和屬性類型:

/**
根據(jù)要存儲進(jìn)入數(shù)據(jù)庫中的類,獲取該類的屬性 以及 該類存儲進(jìn)入數(shù)據(jù)庫后屬性對應(yīng)的 數(shù)據(jù)庫存儲類型
@param class 要存儲的類
*/
- (void)disposePropertyWithClass:(Class)class
{
if (class && class != [NSObject class] && class != [MySqlStatementManager class]) {
unsigned int outCount = 0;
Ivar *ivars = class_copyIvarList(class, &outCount);
for (int i = 0 ; i < outCount; i ++ ) {
Ivar ivar = ivars[i];
//運(yùn)行時獲取屬性名
NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)];
//去掉屬性名開頭中的下劃線
if ([key rangeOfString:@"_"].location == 0) {
key = [key substringFromIndex:1];
}
//運(yùn)行時獲取類型
NSString *type = [NSString stringWithUTF8String:ivar_getTypeEncoding(ivar)];
NSCharacterSet *set = [NSCharacterSet characterSetWithCharactersInString:@"@/:;()¥「」"、[]{}#%-*+=_\\|~<>$€^?’@#$%^&*()_+’\""];
type = [type stringByTrimmingCharactersInSet:set];
if (!self.propertyDic[type]) {
//如果屬性類型不是七種基本數(shù)據(jù)類型,也不是int short 等常量類型,那么顯示如下LOG提示
NSLog(@"數(shù)據(jù)庫不支持%@類中為%@類型的%@屬性存儲,該屬性數(shù)據(jù)將會丟失,請重新建立該類屬性類型",NSStringFromClass(class),key,type);
}else{
// 將屬性中的類型和數(shù)據(jù)庫中的一些類型進(jìn)行配對
NSString *sqlSaveType = self.propertyDic[type];
[self.propertyListDic setValue:sqlSaveType forKey:key];
}
}
free(ivars);
}
}

這里用到了self.propertyDic 和 self.propertyListDic,解釋一下:
self.propertyDic 這個字典是用來將我們常用的所有類型按照:我們Model中各個屬性用的類型【KEY】----各個屬性應(yīng)該對應(yīng)的數(shù)據(jù)庫存儲類型【Value】先前就已經(jīng)初始化好的一個固定不變的字典,意義就是根據(jù)我們用的類型來取到數(shù)據(jù)庫中這個類型對應(yīng)的存儲類型
self.propertyListDic 這個字典是用來記錄我們已經(jīng)處理分析好的一些屬性所應(yīng)該對應(yīng)的數(shù)據(jù)庫存儲類型,那么它最后的形式就是:我們Model中各個屬性名稱【KEY】----數(shù)據(jù)庫中各個屬性的存儲類型【Value】

然后我分析之后我們的self.propertyDic應(yīng)該是這樣:

// 結(jié)合運(yùn)行時獲取model對應(yīng)的屬性類型 ( KEY ),然后這些類型將在數(shù)據(jù)庫中以以下 (Value) 類型進(jìn)行存儲
- (NSDictionary *)propertyDic
{
if (_propertyDic == nil) {
_propertyDic = @{@"NSString" : @"varchar(1024)",
@"NSMutableString" : @"varchar(1024)",
@"NSArray" : @"blob",
@"NSMutableArray" : @"blob",
@"NSDictionary" : @"blob",
@"NSMutableDictionary" : @"blob",
@"NSData" : @"blob",
@"NSMutableData" : @"blob",
@"NSDate" : @"datetime",
@"NSNumber" : @"varchar(1024)",
@"c" : @"varchar(128)",    // 可能為BOOL 類型或者 char 類型
@"i" : @"int",    // Int 類型
@"s" : @"int",    // Short 類型
@"f" : @"float",    // Float 類型
@"d" : @"numeric",    // Double 類型
@"q" : @"int",    // Long 類型
@"B" : @"bit",    // iphone上BOOL類型運(yùn)行時獲取的類型名為 B, mac環(huán)境獲取的是 ‘c’
@"" : @"",};
}
return _propertyDic;
}

解釋說明:可能已經(jīng)有細(xì)心的小伙伴發(fā)現(xiàn)了,我的_propertyDic中設(shè)置的數(shù)組存儲類型為blob【二進(jìn)制大數(shù)據(jù)】,我知道這樣做不妥,但是SQLite是不支持?jǐn)?shù)組的存儲的,而儲存數(shù)組我認(rèn)為的最佳方法是再建一張表,將這個數(shù)組用這一張表存起來,這其中又會涉及到數(shù)據(jù)庫主鍵【PRIMARY KEY】的知識,取出數(shù)據(jù)時再通過聯(lián)表查詢才能取出來一個我們所需要的數(shù)據(jù)模型【不知道有沒有對數(shù)據(jù)庫很熟悉的小伙伴有不同的看法,歡迎來一起交流】,這還只是model中只有一個數(shù)組的情況下,如果數(shù)組中存儲的數(shù)組,那就更尷尬了,可能一個Model要對應(yīng)N張表聯(lián)查才能取出來完整的數(shù)據(jù),當(dāng)然,數(shù)據(jù)庫也是不支持OC字典存儲的,所以剩下的場景大家自己腦補(bǔ)吧

關(guān)于存儲類型的結(jié)論:綜上情景的分析,為了偷懶【請?jiān)徫胰绱酥卑住?,我選擇了直接將數(shù)組歸檔為二進(jìn)制數(shù)據(jù)存儲進(jìn)入數(shù)據(jù)庫??,有更好方法的歡迎評論區(qū)交流

那么繼續(xù)吧,上面我們已經(jīng)通過_propertyDic和_propertyListDic分析出了Model中存在的所有屬性名稱以及該屬性應(yīng)該在數(shù)據(jù)庫中以何種類型存儲,接下來就是生成SQL字符串了

因?yàn)镾QL語句眾多,這里我以建表的SQL為例,其他的大家可以自己去思考怎么生成SQL:

#pragma mark - 建表
- (NSString *)greatTableWithTableName:(NSString *)tableName andClass:(Class)modelClass
{
    [self disposePropertyWithClass:modelClass];
    
    __block NSString *greatSql = [NSString stringWithFormat:@"CREATE TABLE IF NOT EXISTS %@(",tableName];
    
    [self.propertyListDic enumerateKeysAndObjectsUsingBlock:^(id  _Nonnull key, id  _Nonnull obj, BOOL * _Nonnull stop) {
       
       greatSql = [NSString stringWithFormat:@"%@%@ %@,",greatSql,key, obj];
        
    }];
    
    greatSql = [greatSql substringToIndex:greatSql.length - 1];
    //補(bǔ)上結(jié)尾一個括號
    greatSql = [NSString stringWithFormat:@"%@)",greatSql];
    
    return greatSql;
}

調(diào)用如下:

MySqlStatementManager *manger = [[MySqlStatementManager alloc] init];
    NSString *sql = [manger greatTableWithTableName:@"測試" andClass:[TestModel class]];
    NSLog(@"TestModel 對應(yīng)的數(shù)據(jù)庫 建表語句如下 %@",sql);
數(shù)據(jù)庫 建表語句
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容