(原创)发布一个C++版本的ORM库SmartDB(一)
先簡單說說ORM的優(yōu)點:
SmartDB基礎(chǔ)庫
java和c#中有很多ORM框架,如c#中的entity framework、Linq to Sql、NHibernate等,java中有Hibernate、Mybatis等等,其它各種開源的ORM庫多如牛毛。這應(yīng)該得益于托管語言在這方面的優(yōu)勢。然而,c++的世界中,卻鮮有ORM框架,c++中比較知名的ORM庫應(yīng)該是ODB了,但是ODB的使用比較麻煩,需要三個庫,寫的ORM代碼還需要ODB專門編譯一下(不知道我說的是否確切),總之使用起來比較麻煩,讓人望而卻步。我的項目中本來只需要用sqlite,但是考慮到后期可能會更換數(shù)據(jù)庫,所以想搞一個ORM庫,但時間又比較緊,覺得短時間內(nèi)搞不定ODB,于是放棄,選擇最簡單穩(wěn)妥的作法,先只封裝sqlite,目標是封裝成簡潔,統(tǒng)一的接口,即使到后面更換數(shù)據(jù)庫時,我也不需要改應(yīng)用層的代碼,只要替換底層數(shù)據(jù)庫封裝模塊就行了。在這個目標的指引下我很快完成了基礎(chǔ)庫SmartDB的封裝,如下是SmartDB的操作接口。
bool Open(string dbName);//打開數(shù)據(jù)庫 bool Close();//關(guān)閉數(shù)據(jù)庫 bool Excecute(const std::string && query); //直接執(zhí)行sql語句 bool ExcecuteBulk(const Args && ... args); //批量操作的sql語句 bool Prepare(const std::string && query); //批量操作之前的準備 bool Excecute(const std::string && query, const Args && ... args); //執(zhí)行帶占位符的sql語句 R ExecuteScalar(const std::string &query, const Args &... args); //返回函數(shù)執(zhí)行的一個值, 執(zhí)行簡單的匯聚函數(shù),如select count(*), select max(*)等 bool GetTable(const string &query, T &table, TableRange* tableRange, const Args &... args); //返回一個通用的表結(jié)構(gòu) int GetRowCount(const string &query, const TableRange* tableRange, const Args &... args);//獲取表的總行數(shù)這些接口是直接接受sql語句和參數(shù)列表的,所以理論上它可以完成所有的數(shù)據(jù)庫操作。目前內(nèi)部只支持sqlite,我自測了一下基礎(chǔ)庫的性能。在我的雙核6G的臺式機下測試了一下插入速度和讀取速度,一個八字段的表(int和doble類型),批量插入速度大概為25W條/s,讀取速度大概為11W條/s。這些已經(jīng)滿足了我的需求,但是我覺得可以在這個基礎(chǔ)上做得更多,即支持ORM,我期望我的ORM能達到類似于entity framework的調(diào)用方式,比ODB更好用(比如智能提示、鏈式調(diào)用等),而性能又不至于降低太多。
EF中查詢方式 DemoDBEntities context = new DemoDBEntities(); ObjectQuery<BlogMaster> query = context.CreateObjectSet<BlogMaster>().Where("it.UserId > @UserId",new ObjectParameter("UserId", 6)) .OrderBy("it.UserId desc");List<BlogMaster> list = query.ToList();SmartDBEX ORM庫
c++中ORM庫的實現(xiàn)思路: ORM的關(guān)鍵是如何將業(yè)務(wù)實體轉(zhuǎn)成sql語句以及表的原始數(shù)據(jù)如何轉(zhuǎn)成業(yè)務(wù)實體,它們橫在數(shù)據(jù)庫和實體之間的兩道鴻溝,因此,如何通過一個結(jié)構(gòu)體去自動生成sql腳本是第一個需要解決的問題,如何將原始表轉(zhuǎn)成業(yè)務(wù)實體是第二個需要解決的問題。我覺得問題的關(guān)鍵在結(jié)構(gòu)體,我需要結(jié)構(gòu)體提供一些信息給我,比如,字段類型,字段名,字段值等信息,這在c#或者java中可以通過反射就可以得到,但c++沒有反射,是無法自動就得到這些信息的,不過C++雖然沒有托管語言的這些優(yōu)勢,但卻有自己的優(yōu)勢--模板元,可以通過模板元來獲取這些信息,通過這些信息我就可以自動生成sql腳本了;第二個問題則是通過boost的variant和結(jié)構(gòu)體中的賦值函數(shù)來解決。
總之,我通過在結(jié)構(gòu)體上做文章,最終達到了我的目標,而且我會保證結(jié)構(gòu)體不會復(fù)雜,盡可能少的讓用戶去寫代碼,只需要提供幾個簡單的函數(shù)就可以實現(xiàn)ORM了。我是在基礎(chǔ)庫SmartDB之上擴展了ORM功能,最終的ORM庫為SmartDBEX, 它內(nèi)部是調(diào)用了SmartDB實現(xiàn)具體功能。這樣做的目的是即可使用操作sql的接口,又可使用ORM接口,用戶根據(jù)自己選擇即可。
我在臺式機上測了一下SmartDBEX的性能:還是之前的表,批量插入速度為25W條/s,讀取并轉(zhuǎn)成實體列表的速度為2.3W條/s。比較滿意的是批量插入速度沒有降低,差強人意的是讀取實體列表的速度降低了不少,性能損耗在兩個地方:原始表的字段和實體的字段匹配和原始表字段賦值給實體。沒辦法,ORM這里必須要損耗一些性能。2.3W條/s的讀取速度對一般應(yīng)用來說已經(jīng)可以滿足要求了。
如何使用SmartDB和SmartDBEX
SmartDB的測試程序
/*創(chuàng)建表*/ void TestCreateTable(SmartDB& db) { const string sqlcreat = "CREATE TABLE if not exists TestInfoTable(ID INTEGER NOT NULL, KPIID INTEGER, CODE INTEGER, V1 INTEGER, V2 INTEGER, V3 REAL);"; if (!db.Excecute(sqlcreat)) return; }/*單條插入*/ void TestInsert(SmartDB& db) { const string sqlinsert = "INSERT INTO TestInfoTable(ID, KPIID, CODE, V1, V2, V3) VALUES(1, 2, 3, 4, 5, 6.032);"; if (!db.Excecute(sqlinsert)) return;const string sqlinsert1 = "INSERT INTO TestInfoTable(ID, KPIID, CODE, V1, V2, V3) VALUES(?, ?, ?, ?, ?, ?);"; int n = 2; string str = "3.1423"; if (!db.Excecute(sqlinsert1, n, n, n, n, n, str)) return; }/*批量插入, 啟用事務(wù)*/ void TestBulkInsert(SmartDB& db) { const string sqlinsert = "INSERT INTO TestInfoTable(ID, KPIID, CODE, V1, V2, V3) VALUES(?, ?, ?, ?, ?, ?);"; timer t; Transaction transaction(&db); //開始事務(wù) db.Prepare(sqlinsert); //準備sql,含占位符 bool ret = false; for (size_t i = 0; i < 1000000; i++) { ret = db.ExcecuteBulk(i, i, i, i, i, i); if (!ret) break; }if (ret) transaction.Commit(); //提交事務(wù) else transaction.RollBack(); //回滾 cout << t.elapsed() << endl; }?
SmartDBEX的測試程序
void TestORM(SmartDB& db) { SmartDBEx dbex(db); //創(chuàng)建表 auto r = dbex.Create<TestInfo>(); //查詢 auto& query = dbex.Query<TestInfo>().Where(Var(TestInfo::CODE < 10 and TestInfo::ID < 10)); vector<TestInfo> v; bool b = dbex.Get<TestInfo>(v);//獲取總行數(shù) int count = dbex.Count<TestInfo>().Scalar(); cout << count << endl;//單條插入 r = dbex.Insert(t);//批量插入 TestInfo t = {1,2,3,4,5,6}; Transaction tans(&db); r = dbex.Prepare<TestInfo>(); for (size_t i = 0; i < 1000000; i++) { r = dbex.BulkInsert(t); } r = tans.Commit();//刪除 r = dbex.Delete<TestInfo>()(); }
這里我們不妨對比一下ODB的調(diào)用方式,ODB的查詢方式:
typedef odb::query<person> query; typedef odb::result<person> result;{ transaction t (db->begin ());result r (db->query<person> (query::age > 30));for (result::iterator i (r.begin ()); i != r.end (); ++i) { cout << "Hello, " << i->first () << "!" << endl; }t.commit (); }SmartDBEX的查詢方式:
auto& query = dbex.Query<TestInfo>().Where(Var(TestInfo::CODE < 10 and TestInfo::ID < 10)); vector<TestInfo> v; bool b = dbex.Get<TestInfo>(v);呵呵,有點像吧,但哪個更直觀簡潔呢?
有待完善的地方
犧牲了好幾個周末的時間做這個ORM,主要是ORM的關(guān)鍵技術(shù)的思路和實現(xiàn)花了不少時間,今天終于做完了第一個版本,算是對前段時間的思考和研究做個總結(jié)和交代吧。不過由于是業(yè)余時間開發(fā),時間和精力有限,ORM接口目前只有一些基本功能,還不太完善,而且目前內(nèi)部只支持sqlite,至于其它數(shù)據(jù)庫的支持,等到后面再繼續(xù)完善了。另外,庫內(nèi)部沒有采用緩存和延遲加載等技術(shù),不是不能加,是暫時沒有時間和精力去繼續(xù)完善了,而且性能已經(jīng)滿足我的要求了,優(yōu)化完善的工作來日方長吧;后期還可以考慮支持結(jié)構(gòu)體通過文件配置的方式生成,進一步增強靈活性。
歡迎使用
歡迎大家下載測試工程和源碼并試用,發(fā)現(xiàn)問題請報告給我,我好完善。也期待有人愿意在此基礎(chǔ)上和我一起完善它,使它成為一個優(yōu)秀的c++ORM庫,讓c++開發(fā)人員的日子變得更美好,讓c++的世界更加絢麗多彩。
源碼下載地址
c++11 boost技術(shù)交流群:296561497,歡迎大家來交流技術(shù)。
轉(zhuǎn)載于:https://www.cnblogs.com/qicosmos/p/3342037.html
總結(jié)
以上是生活随笔為你收集整理的(原创)发布一个C++版本的ORM库SmartDB(一)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 设计模式-工厂模式学习(初级)
- 下一篇: OpenLdap 相关命令