7天用Go動手寫/從零實現ORM框架GeeORM
0 目錄
- 第一天:database/sql 基礎 | Code
- 第二天:物件表結構對映 | Code
- 第三天:記錄新增和查詢 | Code
- 第四天:鏈式操作與更新刪除 | Code
- 第五天:實現鉤子(Hooks) | Code
- 第六天:支援事務(Transaction) | Code
- 第七天:資料庫遷移(Migrate) | Code
1 談談 ORM 框架
物件關係對映(Object Relational Mapping,簡稱ORM)是通過使用描述物件和資料庫之間對映的後設資料,將面嚮物件語言程式中的物件自動持久化到關聯式資料庫中。
那物件和資料庫是如何對映的呢?
資料庫 | 物件導向的程式語言 |
---|---|
表(table) | 類(class/struct) |
記錄(record, row) | 物件 (object) |
欄位(field, column) | 物件屬性(attribute) |
舉一個具體的例子,來理解 ORM。
CREATE TABLE `User` (`Name` text, `Age` integer);
INSERT INTO `User` (`Name`, `Age`) VALUES ("Tom", 18);
SELECT * FROM `User`;
第一條 SQL 語句,在資料庫中建立了表 User
,並且定義了 2 個欄位 Name
和 Age
;第二條 SQL 語句往表中新增了一條記錄;最後一條語句返回表中的所有記錄。
假如我們使用了 ORM 框架,可以這麼寫:
type User struct {
Name string
Age int
}
orm.CreateTable(&User{})
orm.Save(&User{"Tom", 18})
var users []User
orm.Find(&users)
ORM 框架相當於物件和資料庫中間的一個橋樑,藉助 ORM 可以避免寫繁瑣的 SQL 語言,僅僅通過操作具體的物件,就能夠完成對關係型資料庫的操作。
那如何實現一個 ORM 框架呢?
CreateTable
方法需要從引數&User{}
得到對應的結構體的名稱 User 作為表名,成員變數 Name, Age 作為列名,同時還需要知道成員變數對應的型別。Save
方法則需要知道每個成員變數的值。Find
方法僅從傳入的空切片&[]User
,得到對應的結構體名也就是表名 User,並從資料庫中取到所有的記錄,將其轉換成 User 物件,新增到切片中。
如果這些方法只接受 User 型別的引數,那是很容易實現的。但是 ORM 框架是通用的,也就是說可以將任意合法的物件轉換成資料庫中的表和記錄。例如:
type Account struct {
Username string
Password string
}
orm.CreateTable(&Account{})
這就面臨了一個很重要的問題:如何根據任意型別的指標,得到其對應的結構體的資訊。這涉及到了 Go 語言的反射機制(reflect),通過反射,可以獲取到物件對應的結構體名稱,成員變數、方法等資訊,例如:
typ := reflect.Indirect(reflect.ValueOf(&Account{})).Type()
fmt.Println(typ.Name()) // Account
for i := 0; i < typ.NumField(); i++ {
field := typ.Field(i)
fmt.Println(field.Name) // Username Password
}
reflect.ValueOf()
獲取指標對應的反射值。reflect.Indirect()
獲取指標指向的物件的反射值。(reflect.Type).Name()
返回類名(字串)。(reflect.Type).Field(i)
獲取第 i 個成員變數。
除了物件和表結構/記錄的對映以外,設計 ORM 框架還需要關注什麼問題呢?
1)MySQL,PostgreSQL,SQLite 等資料庫的 SQL 語句是有區別的,ORM 框架如何在開發者不感知的情況下適配多種資料庫?
2)如何物件的欄位發生改變,資料庫表結構能夠自動更新,即是否支援資料庫自動遷移(migrate)?
3)資料庫支援的功能很多,例如事務(transaction),ORM 框架能實現哪些?
4)…
2 關於 GeeORM
資料庫的特性非常多,簡單的增刪查改使用 ORM 替代 SQL 語句是沒有問題的,但是也有很多特性難以用 ORM 替代,比如複雜的多表關聯查詢,ORM 也可能支援,但是基於效能的考慮,開發者自己寫 SQL 語句很可能更高效。
因此,設計實現一個 ORM 框架,就需要給功能特性排優先順序了。
Go 語言中使用比較廣泛 ORM 框架是 gorm 和 xorm。除了基礎的功能,比如表的操作,記錄的增刪查改,gorm 還實現了關聯關係(一對一、一對多等),回撥外掛等;xorm 實現了讀寫分離(支援配置多個資料庫),資料同步,匯入匯出等。
gorm 正在徹底重構 v1 版本,短期內看不到釋出 v2 的可能。相比於 gorm-v1,xorm 在設計上更清晰。GeeORM 的設計主要參考了 xorm,一些細節上的實現參考了 gorm。GeeORM 的目的主要是瞭解 ORM 框架設計的原理,具體實現上魯棒性做得不夠,一些複雜的特性,例如 gorm 的關聯關係,xorm 的讀寫分離沒有實現。目前支援的特性有:
- 表的建立、刪除、遷移。
- 記錄的增刪查改,查詢條件的鏈式操作。
- 單一主鍵的設定(primary key)。
- 鉤子(在建立/更新/刪除/查詢之前或之後)
- 事務(transaction)。
- …
GeeORM
分7天實現,每天完成的部分都是可以獨立執行和測試的,就像搭積木一樣,一個個獨立的特性組合在一起就是最終的 ORM 框架。每天的程式碼在 100 行左右,同時配有較為完備的單元測試用例。
附 推薦閱讀
原文地址: 7天用Go從零實現ORM框架GeeORM - 極客兔兔
知乎專欄: Go語言 - 極客兔兔
關注微博: @極客兔兔
相關文章
- 7天用Go動手寫/從零實現RPC框架GeeRPCGoRPC框架
- [7 天用 Go 從零實現系列] 之 Web 框架 GeeGoWeb框架
- 01 . Go之從零實現Web框架(類似Gin)GoWeb框架
- 從零手寫實現 nginx-21-modules 模組Nginx
- 從零手寫實現 nginx-27-return 指令Nginx
- 從零開始手寫Koa2框架框架
- 從零手動實現簡易TomcatTomcat
- 手寫開源ORM框架介紹ORM框架
- 從零手寫實現 tomcat-03-基本的 socket 實現Tomcat
- 從零手寫實現 nginx-29-try_files 指令Nginx
- 從零手寫實現 nginx-35-proxy_pass netty 如何實現?NginxNetty
- 從零手寫實現 nginx-03-nginx 基於 Netty 實現NginxNetty
- 從零手寫實現 tomcat-05-servlet 處理支援TomcatServlet
- 從零手寫實現 tomcat-09-servlet 處理類TomcatServlet
- 從零手寫實現 tomcat-11-filter 過濾器TomcatFilter過濾器
- 從零手寫實現 nginx-06-資料夾內容的自動索引展示Nginx索引
- 從零開始手寫一個微前端框架-渲染篇前端框架
- 從零開始實現一個RPC框架(零)RPC框架
- 從零手寫逐步實現Promise A+標準的所有方法Promise
- java 從零開始手寫 RPC (01) 基於 websocket 實現JavaRPCWeb
- 從零手寫實現 nginx-23-nginx 對於 cookie 的操作NginxCookie
- 從零手寫實現 apache Tomcat-01-入門介紹ApacheTomcat
- 核心技術靠化緣是要不來的——自己動手寫ORM框架ORM框架
- HarmonyOS(鴻蒙)運動手錶—從零實現投骰子小遊戲鴻蒙遊戲
- 從零開始寫JavaScript框架(二)JavaScript框架
- 從零開始寫JavaScript框架(一)JavaScript框架
- 從零手寫實現 nginx-33-http_proxy 代理驗證測試NginxHTTP
- 從零手寫實現 nginx-25-directive map 條件判斷指令Nginx
- 從零手寫實現 nginx-09-compress http 檔案壓縮NginxHTTP
- 手寫 Hibernate ORM 框架 00-hibernate 簡介ORM框架
- 手寫 Hibernate ORM 框架 05-基本效果測試ORM框架
- 從零手寫實現 nginx-31-load balance 負載均衡介紹Nginx負載
- 從頭開始,手寫android應用框架(一)Android框架
- 手擼ORM淺談ORM框架之Query篇ORM框架
- 手擼ORM淺談ORM框架之Delete篇ORM框架delete
- 手擼ORM淺談ORM框架之Update篇ORM框架
- 從零開始用 proxy 實現 Mobx
- 手寫 Hibernate ORM 框架 01-註解常量定義ORM框架