第二期Go開源說實錄:GORM 剖析與最佳實踐

項目簡介

GORM 是一款 設計簡潔、功能強大、自由擴展的全功能 ORM ,遵循了 API 精簡、測試優先、最小驚訝、靈活擴展、無依賴  可信賴的設計原則。GORM做爲一個全功能的ORM,提供了完善的功能,例如:

  • 關聯:一對一、一對多、單表自關聯、多態關聯;Preload、Joins 預加載;關聯模式

  • 事務:嵌套事務, Save Point

  • Hooks、Callbacks 自由擴展

  • 多數據庫、讀寫分離、Prometheus、Prepared Stmt、查詢優化器、批量數據處理、代碼共享、子查詢、DryRun

  • SQL Builder、Smart Migration、複合主鍵、自定義類型 (JSON等)、SQL 表達式查詢創建更新、虛擬字段…

  • 真 • 跨數據庫兼容等功能

開源歷程

 GORM 第一次大概是 2013 年開源,當時在用 Go 寫一套支付系統,當時 Go 的生態圈也不太成熟,總感覺別人家的輪子的不夠圓,所以準備自己造個更圓的輪子,於是就有了GORM。GORM 在開源過程中收益衆多,收集了很多反饋讓我們向着更好不斷迭代,還收集了特別多的邊緣情況,也讓我讀了各數據庫的相關文檔好多遍,極大的幫我們避免了不少的潛在問題,讓我們的系統穩定性提高了很多,所以開源給我們的收益還是挺大的。

 GORM V2.0 的開發起因於 2020 年春節疫情假期剛好空出些時間,然後開始了 2.0 版本的開發計劃。2.0 版本是吸取了 1.0 版本的一些經驗教訓,從零開始完全重寫的版本,在重寫的過程中性能、靈活性、擴展性都得到了很大的提升。

分享提要

本次分享主要從 GORM 的 CRUD 基本操作入手,然後延伸講解了一些高階的使用方法,例如使用 Map 更新創建查詢、批量插入/更新、SQL 表達式、默認值、零值問題、關聯操作、SQL Builder、命名參數等等。

項目倉庫:https://github.com/go-gorm/gorm

項目文檔:https://gorm.cn

演講視頻:

https://www.bilibili.com/video/BV1ST4y1T7NR

演講PPT:   

https://www.slideshare.net/JinzhuZhang2/gorm-241179148

補充介紹

本次分享因爲時間所限主要講解了普通用戶最常用的一些 CRUD 的功能介紹,還有挺多 GORM 的特性沒來得及和大家探索,不過 GORM 的文檔提供了比較全面的介紹,歡迎大家參考文檔瞭解詳情,這裏給大家羅列了一個列表參考。

做爲普通用戶除了上面的介紹你還必須要知道的:

  • Method Chain 的線程安全

    https://gorm.cn/zh_CN/docs/method_chaining.html

  • 防止 SQL 注入

    https://gorm.cn/zh_CN/docs/security.html

  • 錯誤處理

    https://gorm.cn/zh_CN/docs/error_handling.html

做爲普通用戶你最好知道的:

  • GORM 優於配置的一些約定

    https://gorm.cn/zh_CN/docs/models.html#Conventions

  • 如何給字段來配置讀寫權限

    https://gorm.cn/zh_CN/docs/models.html#field_permission

  • 給多字段配置時間追蹤

    https://gorm.cn/zh_CN/docs/models.html#time_tracking

  • 自關聯定義 (一對一,多對多,一對多,單表自關聯,多態)、自定義 foreign key, reference、複合外鍵、自定義 JoinTable

  • Query 的一些 API: Pluck, FirstOrInit, FirstOrCreate (Assign, Attrs)

  • https://gorm.cn/zh_CN/docs/advanced_query.html#FirstOrInit

  • Migrations

    https://gorm.cn/zh_CN/docs/migration.html

  • 字段的 Tags 特殊配置支持

    https://gorm.cn/zh_CN/docs/models.html#tags

  • 如何使用 Context

    https://gorm.cn/zh_CN/docs/context.html

  • Transactions, Nested Transactions, Save Point, RollbackTo to Saved Point

    https://gorm.cn/zh_CN/docs/transactions.html

  • Gorm Config (跳過默認事務、修改命名策略、修改當前時間函數、只生成 SQL 不執行模式、Prepared Stmt 加速模式等等)

    https://gorm.cn/zh_CN/docs/gorm_config.html

  • Session 模式概念及其配置 (如:跳過默認事務、修改命名策略、修改當前時間函數、只生成 SQL 不執行模式、Prepared Stmt 加速模式等等)

    https://gorm.cn/zh_CN/docs/session.html

  • 定義索引、複合索引、優先索引、約束等

    https://gorm.cn/zh_CN/docs/indexes.html https://gorm.cn/zh_CN/docs/constraints.html

  • 數據庫連接池配置

    https://gorm.cn/zh_CN/docs/generic_interface.html

  • 數據庫的不同連接參數,以 mysql 爲例子

    https://github.com/go-gorm/mysql#gorm-mysql-driver

  • Hooks 介紹

    https://gorm.cn/zh_CN/docs/hooks.html

做爲資深用戶你還需要知道:

  • 讀寫分離 / 多數據庫

    https://gorm.cn/zh_CN/docs/dbresolver.html

  • 使用查詢優化器,指定索引查詢

    https://gorm.cn/zh_CN/docs/hints.html

  • Prometheus 集成

    https://gorm.cn/zh_CN/docs/prometheus.html

  • 使用複合主鍵

    https://gorm.cn/zh_CN/docs/composite_primary_key.html

  • 分庫分表 / 代碼共享

    https://gorm.cn/zh_CN/docs/scopes.html

  • Embedded Struct 定義共享 struct (參考 embedded tag)

  • 自定義數據類型(json 等數據類型支持)

    https://gorm.cn/zh_CN/docs/data_types.html

  • 如何提升性能

    https://gorm.cn/zh_CN/docs/performance.html

如果你是一個定製開發者你需要知道的:

  • 多看源碼,貢獻社區 ????

    https://github.com/go-gorm/gorm

  • 熟悉 Statement & Clause 概念

    https://www2.slideshare.net/JinzhuZhang2/gorm-gopher-china

  • 定製 Callbacks 插件

    https://gorm.cn/zh_CN/docs/write_plugins.html

  • 瞭解如何定製 driver 實現特殊需求 (各 driver 源碼,

    https://gorm.cn/zh_CN/docs/write_driver.html)

  • 如何定製 logger  

    https://gorm.cn/zh_CN/docs/logger.html

  • Set/Get/InstanceSet/InstanceGet Callback 傳遞參數

    https://gorm.cn/zh_CN/docs/settings.html

問答環節

1.如何設置表名前綴

可以通過配置 gorm.Config 的 NamingStrategy 實現需求,具體參考:

https://gorm.io/docs/gorm_config.html#NamingStrategy

db, err := gorm.Open(sqlite.Open("gorm.db"), &gorm.Config{
NamingStrategy: schema.NamingStrategy{
TablePrefix: "t_", // NOTE 這裏
SingularTable: true,
SingularTable: true,
},
})

默認的 NamingStrategy 可以實現一些常見的配置,如果不能滿足需求的話,可以選擇自定義 Namer 的 interface,例如:

type Namer interface {
TableName(table string) string
ColumnName(table, column string) string
JoinTableName(table string) string
RelationshipFKName(Relationship) string
CheckerName(table, column string) string
IndexName(table, column string) string
}

2.db Session 是什麼?他和 db connection 有什麼關係?

DB Session 只是一個變量,和 db Connection 無關。對於一個新的 DB Session 來說,還會共享原來的連接池。他的作用主要在於可用來給每個 Session 做一些常見的配置,常見配置例如跳過默認事務、修改命名策略、修改當前時間函數、只生成 SQL 不執行模式、Prepared Stmt 加速模式等,詳情請參考

https://gorm.io/docs/session.html

還有一個要注意的問題是 Method Chain 的線程安全問題,可參考

https://gorm.io/docs/method_chaining.html

3.clause.Associations 進行級連刪除時發生錯誤會怎麼樣?

GORM 默認的所有的創建/更新/刪除都在一個事務內,如果在當中發生錯誤的話,會進行整體回滾。

另外關於級連刪除,他只會刪除依賴他的關聯 (has one, has many, many2many 中間表),而不會刪除他依賴的關聯 (belongs to, many2many 源數據),舉個例子,如果有一個用戶,他在一個公司工作,他還擁有一個寵物,在對他進行級聯刪除的時候,只會刪除這個用戶,以及他所擁有的寵物,而不會刪除這個人的公司。

4.如何在測試中 mock 數據

可以使用 https://github.com/DATA-DOG/go-sqlmock 先建立一個 connection,例如

import "github.com/DATA-DOG/go-sqlmock"
mockdb, mock, err := sqlmock.New()

然後在測試時可以通過使用現有連接的方式來配置 GORM,如果使用 mysql 數據庫,可以指定 mysql 的 driver,其它數據庫需要使用相應的 driver:

import (
"gorm.io/gorm"
"gorm.io/driver/mysql"
)
gormDB, err := gorm.Open(mysql.New(mysql.Config{
Conn: mockdb,
}), &gorm.Config{})

不過個人還是推薦能使用真實數據庫測試就使用真實數據庫進行測試,畢竟行爲還是有些不一樣,使用真實數據庫可以更好的、更早的發現線上問題。

5.可以 Preload 預加載的時候使用其它的數據庫麼?例如 postgres, mongodb

默認不支持從其它的數據庫預加載,不過支持從同一種數據庫的其它服務 IP 上加載,這部分可以參考 db resolver,多數據庫模式 https://gorm.io/docs/dbresolver.html

如果想支持其它類的數據庫或從緩存服務器上查詢數據的話,需要通過自定義 callbacks,然後在 callbacks 里根據從  db.Statement 獲取當前的條件,然後根據這些條件從相應的其它的數據庫、緩存數據庫中查詢出相應數據並賦值回原對象中。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章