通用ORM的設計與實現
介紹
我們通用的ORM,基本模式都是想要脫離資料庫的,幾乎都在程式語言層面建立模型,由程式去與資料庫打交道。雖然脫離了資料庫的具體操作,但我們要建立各種模型文件,用程式碼去寫表之間的關係等等操作,讓初學者一時如墜雲霧。我的想法是,將關係資料庫擁有的完善設計工具之優勢,來實現資料設計以提供結構資訊,讓json物件自動對映成為標準的SQL查詢語句。只要我們理解了標準的SQL語言,我們就能夠完成資料庫查詢操作。
依賴關係
本專案依賴 本人的 另一個專案 Zjson,此專案提供簡潔、方便、高效的Json庫。該庫使用方便,是一個單檔案庫,只需要下載並引入專案即可。具體資訊請移步gitee-Zjson 或github-Zjson 。
設計思路
ZORM 資料傳遞採用json來實現,使資料標準能從最前端到最後端達到和諧統一。此專案目標,不但在要C++中使用,還要作為動態連結庫與node.js結合用使用,因此希望能像javascript一樣,簡潔方便的操作json。所以先行建立了zjson庫,作為此專案的先行專案。設計了資料庫通用操作介面,實現與底層實現資料庫的分離。該介面提供了CURD標準訪問,以及批量插入和事務操作,基本能滿足平時百分之九十以上的資料庫操作。專案基本目標,支援Sqlite3,Mysql,Postges三種關係資料庫,同時支援windows、linux和macOS。
專案進度
現在已經實現了sqlit3與mysql的所有功能,postgres也做了技術準備。
我選擇的技術實現方式,基本上是最底層高效的方式。sqlit3 - sqllit3.h(官方的標準c介面);mysql - c api (MySQL Connector C 6.1);postgres - pqxx 。
任務列表:
-
[x] Sqlite3 實現
- [x] linux
- [x] windows
- [x] macos
-
[x] Mysql 實現
- [x] linux
- [x] windows
- [x] macos
-
[ ] Pstgre 實現
- [ ] linux
- [ ] windows
- [ ] macos
資料庫通用介面
應用類直接操作這個通用介面,實現與底層實現資料庫的分離。該介面提供了CURD標準訪問,以及批量插入和事務操作,基本能滿足平時百分之九十以上的資料庫操作。
class ZORM_API Idb { public: virtual Json select(string tablename, Json& params, vector<string> fields = vector<string>(), Json values = Json(JsonType::Array)) = 0; virtual Json create(string tablename, Json& params) = 0; virtual Json update(string tablename, Json& params) = 0; virtual Json remove(string tablename, Json& params) = 0; virtual Json querySql(string sql, Json params = Json(), Json values = Json(JsonType::Array), vector<string> fields = vector<string>()) = 0; virtual Json execSql(string sql, Json params = Json(), Json values = Json(JsonType::Array)) = 0; virtual Json insertBatch(string tablename, Json& elements, string constraint = "id") = 0; virtual Json transGo(Json& sqls, bool isAsync = false) = 0; };
例項構造
全域性查詢開關變數:
- DbLogClose : sql 查詢語句顯示開關
- parameterized : 是否使用引數化查詢
Sqlite3:
Json options; options.addSubitem("connString", "./db.db"); //資料庫位置 options.addSubitem("DbLogClose", false); //顯示查詢語句 options.addSubitem("parameterized", false); //不使用引數化查詢 DbBase* db = new DbBase("sqlite3", options);
Mysql:
Json options; options.addSubitem("db_host", "192.168.6.6"); //mysql服務IP options.addSubitem("db_port", 3306); //埠 options.addSubitem("db_name", "dbtest"); //資料庫名稱 options.addSubitem("db_user", "root"); //登記使用者名稱 options.addSubitem("db_pass", "123456"); //密碼 options.addSubitem("db_char", "utf8mb4"); //連線字元設定[可選] options.addSubitem("db_conn", 5); //連線池配置[可選],預設為2 options.addSubitem("DbLogClose", true); //不顯示查詢語句 options.addSubitem("parameterized", true); //使用引數化查詢 DbBase* db = new DbBase("mysql", options);
智慧查詢方式設計
查詢保留字:page, size, sort, fuzzy, lks, ins, ors, count, sum, group
-
page, size, sort, 分頁排序
在sqlit3與mysql中這比較好實現,limit來分頁是很方便的,排序只需將引數直接拼接到order by後就好了。
查詢示例:
Json p; p.addSubitem("page", 1); p.addSubitem("size", 10); p.addSubitem("size", "sort desc"); (new DbBase(...))->select("users", p); 生成sql: SELECT * FROM users ORDER BY age desc LIMIT 0,10
-
fuzzy, 模糊查詢切換引數,不提供時為精確匹配
提供欄位查詢的精確匹配與模糊匹配的切換。
Json p; p.addSubitem("username", "john"); p.addSubitem("password", "123"); p.addSubitem("fuzzy", 1); (new DbBase(...))->select("users", p); 生成sql: SELECT * FROM users WHERE username like '%john%' and password like '%123%'
-
ins, lks, ors
這是最重要的三種查詢方式,如何找出它們之間的共同點,減少冗餘程式碼是關鍵。
-
ins, 資料庫表單欄位in查詢,一欄位對多個值,例:
查詢示例:
Json p; p.addSubitem("ins", "age,11,22,36"); (new DbBase(...))->select("users", p); 生成sql: SELECT * FROM users WHERE age in ( 11,22,26 )
-
ors, 資料庫表多欄位精確查詢,or連線,多個欄位對多個值,例:
查詢示例:
Json p; p.addSubitem("ors", "age,11,age,36"); (new DbBase(...))->select("users", p); 生成sql: SELECT * FROM users WHERE ( age = 11 or age = 26 )
-
lks, 資料庫表多欄位模糊查詢,or連線,多個欄位對多個值,例:
查詢示例:
Json p; p.addSubitem("lks", "username,john,password,123"); (new DbBase(...))->select("users", p); 生成sql: SELECT * FROM users WHERE ( username like '%john%' or password like '%123%' )
-
-
count, sum
這兩個統計求和,處理方式也類似,查詢時一般要配合group與fields使用。
-
count, 資料庫查詢函式count,行統計,例:
查詢示例:
Json p; p.addSubitem("count", "1,total"); (new DbBase(...))->select("users", p); 生成sql: SELECT *,count(1) as total FROM users
-
sum, 資料庫查詢函式sum,欄位求和,例:
查詢示例:
Json p; p.addSubitem("sum", "age,ageSum"); (new DbBase(...))->select("users", p); 生成sql: SELECT username,sum(age) as ageSum FROM users
-
-
group, 資料庫分組函式group,例:
查詢示例:
Json p; p.addSubitem("group", "age"); (new DbBase(...))->select("users", p); 生成sql: SELECT * FROM users GROUP BY age
不等操作符查詢支援
支援的不等操作符有:>, >=, <, <=, <>, =;逗號符為分隔符,一個欄位支援一或二個操作。
特殊處:使用"="可以使某個欄位跳過search影響,讓模糊匹配與精確匹配同時出現在一個查詢語句中
-
一個欄位一個操作,示例:
查詢示例:
Json p; p.addSubitem("age", ">,10"); (new DbBase(...))->select("users", p); 生成sql: SELECT * FROM users WHERE age> 10
-
一個欄位二個操作,示例:
查詢示例:
Json p; p.addSubitem("age", ">=,10,<=,33"); (new DbBase(...))->select("users", p); 生成sql: SELECT * FROM users WHERE age>= 10 and age<= 33
-
使用"="去除欄位的fuzzy影響,示例:
查詢示例:
Json p; p.addSubitem("age", "=,18"); p.addSubitem("username", "john"); p.addSubitem("fuzzy", "1"); (new DbBase(...))->select("users", p); 生成sql: SELECT * FROM users WHERE age= 18 and username like '%john%'
具體使用方法,請參看uint test。
單元測試
有完整功能的單元測試用例,請參見tests目錄下的測試用例。
測試用例執行結果樣例
專案地址
http://gitee.com/zhoutk/zorm 或 http://github.com/zhoutk/zorm
執行方法
該專案在vs2019, gcc7.5, clang12.0下均編譯執行正常。
git clone http://github.com/zhoutk/zorm cd zorm cmake -Bbuild . ---windows cd build && cmake --build . ---linux & macos cd build && make run zorm or ctest
注在linux下需要先行安裝mysql開發庫, 並先手動建立資料庫 dbtest。
在ubuntu下的命令是: apt install libmysqlclient-dev
相關專案
會有一系列專案出爐,網路服務相關,敬請期待...
- SegmentFault 2022 年社群週報 Vol.9
- 社群精選 | 不容錯過的9個冷門css屬性
- 2022最新版 Redis大廠面試題總結(附答案)
- 手寫一個mini版本的React狀態管理工具
- 【vue3原始碼】十三、認識Block
- 天翼雲全場景業務無縫替換至國產原生作業系統CTyunOS!
- JavaScript 設計模式 —— 代理模式
- MobTech簡訊驗證ApiCloud端SDK
- 以羊了個羊為例,淺談小程式抓包與響應報文修改
- 這幾種常見的 JVM 調優場景,你知道嗎?
- 聊聊如何利用管道模式來進行業務編排(下篇)
- 通用ORM的設計與實現
- 如此狂妄,自稱高效能佇列的Disruptor有啥來頭?
- 為什麼要學習GoF設計模式?
- 827. 最大人工島 : 簡單「並查集 列舉」運用題
- 介紹 Preact Signals
- 手把手教你如何使用 Timestream 實現物聯網時序資料儲存和分析
- 850. 矩形面積 II : 掃描線模板題
- Java 併發程式設計解析 | 基於JDK原始碼解析Java領域中的併發鎖,我們可以從中學習到什麼內容?
- 令人困惑的 Go time.AddDate