童辰 蔣超 蔣俊銘
摘 要 C++語言不具備高級語言如Java的反射機制,無法在程序運行時獲取類的結(jié)構(gòu)信息,在開發(fā)數(shù)據(jù)庫交互程序時不具備Hibernate、Mybatis等對象關(guān)系映射能力。Qt是基于C++語言的工具包,提供QProperty宏獲取類結(jié)構(gòu)信息,本文基于該特性,開發(fā)對象關(guān)系映射框架,實現(xiàn)數(shù)據(jù)庫數(shù)據(jù)到類對象的雙向轉(zhuǎn)換,支持?jǐn)?shù)據(jù)庫的增、刪、改、查操作,支持主流的Oracle和Mysql數(shù)據(jù)庫,支持緩存操作,提高訪問效率。同時開發(fā)配套工具自動生成數(shù)據(jù)類代碼。
關(guān)鍵詞 對象關(guān)系映射;Qt;Oracle;Mysql
引言
對象關(guān)系映射(Object Relational Mapping,簡稱ORM)模式是一種為了解決面向?qū)ο笈c關(guān)系數(shù)據(jù)庫存在的互不匹配的技術(shù)。簡單來說,ORM是通過使用類成員變量和數(shù)據(jù)庫之間映射的元數(shù)據(jù),將程序中的對象自動持久化到關(guān)系數(shù)據(jù)庫中。而C++作為主流且歷史悠久的開發(fā)語言,不具備在程序運行時獲取類的結(jié)構(gòu)信息的能力。C++的數(shù)據(jù)類型如何適配Oracle和Mysql的數(shù)據(jù)類型。數(shù)據(jù)庫操作均需要使用Sql語句,程序函數(shù)如何轉(zhuǎn)換為Sql語句。對于頻繁訪問的程序?qū)ο笕绾卧O(shè)計緩存避免訪問數(shù)據(jù)庫提高效率。類文件通常代碼比較多,如何加快代碼開發(fā)速度,這些問題都需要研究討論。
1運行時類結(jié)構(gòu)信息獲取
Qt的類對象在C++的基礎(chǔ)上封裝了類數(shù)據(jù)結(jié)構(gòu)信息并支持運行時獲取接口。首先需要對其實現(xiàn)原理進(jìn)行了解[1]。通常C++的編譯過程為預(yù)處理->編譯->鏈接->運行,Qt編譯的過程中,有一個moc的過程,即為moc->預(yù)處理->編譯->鏈接->運行。在moc過程中,需要識別一些特殊的宏Q_OBJECT、Q_PROPERTY、Q_INVOKABLE,然后根據(jù)頭文件生成moc實現(xiàn)文件,實現(xiàn)文件中包含了類數(shù)據(jù)結(jié)構(gòu)信息并支持運行時獲取接口,獲取接口包括metaObject(),property(),className(),method(),constructor()等。
2程序類型和數(shù)據(jù)庫類型的轉(zhuǎn)換
不同數(shù)據(jù)庫的數(shù)據(jù)類型定義是有差異的,Oracle和Mysql在數(shù)據(jù)類型定義上差異較大,如何屏蔽數(shù)據(jù)庫類型的差異性?可以使用QVariant通用數(shù)據(jù)類型與數(shù)據(jù)庫類型進(jìn)行第一次映射,QVariant類的函數(shù)屏蔽了類型操作細(xì)節(jié),與QSqldatabase類配合,通過加載不同的數(shù)據(jù)庫插件屏蔽了數(shù)據(jù)庫類型差異[2]。
C++常用的數(shù)據(jù)類型有int、double等,Qt也定義了QString、QDatetime等更為方便實用的類對象,對于int、double、QString、QDatetime等常用類型,QVariant能自動根據(jù)數(shù)據(jù)庫類型進(jìn)行轉(zhuǎn)換,但是一些性能或空間限制下,將數(shù)據(jù)庫時間類型設(shè)置為int,則QVariant無法自動轉(zhuǎn)換。本框架使用Q_CLASSINFO()宏進(jìn)行映射,比如上述例子,標(biāo)注類成員為int類型,對應(yīng)的數(shù)據(jù)庫類型為時間類型,在生成數(shù)據(jù)庫sql語句時根據(jù)此映射進(jìn)行轉(zhuǎn)換。
3類成員變量生成數(shù)據(jù)庫Sql語句
數(shù)據(jù)庫支持Sql進(jìn)行增刪改查,限制數(shù)據(jù)庫數(shù)據(jù)可以映射到程序類對象,自然對數(shù)據(jù)庫的增刪改查也要提升到對程序類對象,需要對增刪改查四個動作分別封裝處理函數(shù)。類對象與業(yè)務(wù)相關(guān)所以字段均不一致,而處理函數(shù)參數(shù)只能是一種類型。有兩種解決方案:①使用模板參數(shù)作為函數(shù)入?yún)?,缺點是代碼實現(xiàn)必須編寫在頭文件中,不利用功能更新升級,調(diào)用時需要注明參數(shù)類型,代碼冗長。②抽象公共基類,所有類均繼承該類,利用多態(tài)特性,將基類作為函數(shù)入?yún)?,這樣分離了接口和實現(xiàn),擴展性好。綜合比較選擇方案2。
對數(shù)據(jù)庫數(shù)據(jù)的操作包括增刪改查四種,基本語法基本一致,不同數(shù)據(jù)差異主要在字段不一致,只要保證類成員變量名和數(shù)據(jù)庫表每一個字段名對應(yīng),即可實現(xiàn)數(shù)據(jù)對象到Sql語句的轉(zhuǎn)換,包括如下4種情況。增加數(shù)據(jù),Insert into 表名字段名 values(字段值)。刪除數(shù)據(jù),Delete from 表名 where 主鍵=值。修改數(shù)據(jù),update 表名字段名 values(字段值)。查詢,Select 字段名 from 表名 where 條件 order 條件。從上述可見,不同操作差異的地方就是表名、字段名、主鍵、條件,在類定義中只要定義了前三者即可,條件的可變性較大,可在處理函數(shù)擴展參數(shù),作為入?yún)⑤斎搿?/p>
4程序緩存設(shè)計
程序設(shè)計中經(jīng)常出現(xiàn)核心數(shù)據(jù)頻繁使用,如果每次均從數(shù)據(jù)庫獲取則延時大影響程序的運行效率,前面對數(shù)據(jù)庫的操作已經(jīng)封裝了增刪改查四種函數(shù),則可以在函數(shù)實現(xiàn)中緩存數(shù)據(jù)。當(dāng)增加數(shù)據(jù)到數(shù)據(jù)庫成功同時增加緩存,刪除數(shù)據(jù)庫成功時刪除緩存,修改數(shù)據(jù)庫成功時修改緩存,查詢數(shù)據(jù)庫時同步增加緩存,如此就實現(xiàn)程序使用的每一個數(shù)據(jù)在緩存中均具備且和數(shù)據(jù)庫保持狀態(tài)一致。當(dāng)數(shù)據(jù)量大時,要快速檢索數(shù)據(jù),使用Map結(jié)構(gòu)進(jìn)行存儲,分兩層進(jìn)行檢索,先按照類型再按照每一條數(shù)據(jù)的主鍵值進(jìn)行檢索,從而將檢索效率提高到O(nlgn)時間復(fù)雜度級別。
5類代碼文件生成工具
數(shù)據(jù)類代碼因為包含成員變量的讀寫函數(shù)、元數(shù)據(jù)配置定義、其他配置定義、字段值打印等,代碼量較大,若手寫代碼開發(fā)效率低。通過分析多個數(shù)據(jù)類代碼文件,其差異的地方在于成員變量名、類名、表配置名、主鍵名等,而這些信息均可以從對應(yīng)的數(shù)據(jù)庫表拿到,因此所有的信息來源是數(shù)據(jù)庫表,配合數(shù)據(jù)類代碼模板即可實現(xiàn)完整的數(shù)據(jù)類代碼。生成工具提供數(shù)據(jù)庫表名的輸入框,從數(shù)據(jù)庫表基礎(chǔ)信息中提取字段名、主鍵等信息,替換代碼模板中的占位符,即可生成數(shù)據(jù)類代碼。
6結(jié)束語
本文研究了Qt運行時類結(jié)構(gòu)信息獲取方法,解決數(shù)據(jù)庫類型和程序類型映射問題,提出類成員變量生成Sql語句的方案,進(jìn)一步優(yōu)化程序緩存設(shè)計,研究類代碼文件生成工具,從而實現(xiàn)C++語言下的對象關(guān)系映射框架,極大提高了數(shù)據(jù)庫交互程序的開發(fā)效率和穩(wěn)定性。
參考文獻(xiàn)
[1] Mark S,白建平.Qt高級編程[M].北京:電子工業(yè)出版社,2011:171.
[2] 王維波,栗寶鵑,候春望.Qt 5.9 C++ 開發(fā)指南[M].北京:人民郵電出版社,2018:213.