GORM V2 幾個最實用的功能和升級注意事項
共 6740字,需瀏覽 14分鐘
·
2024-07-19 08:00
▲ 點擊上方"網(wǎng)管叨bi叨"關(guān)注公眾號
最近在自己在開發(fā)個人的新項目,這個項目預(yù)計未來幾個月后能跟大家見面,項目搭建的過程中遇到了ORM版本選擇的問題,經(jīng)過自己仔細斟酌還是選擇了GORM的 V2版作為項目的ORM框架,這個抉擇過程其實就是說服自己不使用的V1的一個心里斗爭。
因為這幾年在公司做的項目都是使用的GORM的V1版本,如果選擇V1的話我只要把以前總結(jié)的那些代碼拿過來改改就能用了,但是因為兩個原因還是選擇了使用GORM V2,下面我先重點說一下這兩個原因,再介紹幾個使用V2版本時大家寫代碼需要注意的破壞性更新。
V2 支持在日志中增加追蹤信息
說實話這個是我選擇升級到V2的一個主要原因, 良好的基礎(chǔ)框架是一個項目成功的必備因素,GORM V1版本開發(fā)的Logger接口中我們是沒有辦法把請求上下文傳遞進去的。
在使用GORM的時候,如果我們想把GORM產(chǎn)生的日志記錄到項目統(tǒng)一的應(yīng)用日志中的時,需要自己去實現(xiàn)GORM提供的logger 接口。
V1版本的GROM的 logger 接口長這個樣子,僅僅提供了一個Print方法,Print方法的參數(shù)都是 create、updates、query 這些方法的回調(diào)中傳遞過去的,我們并沒有辦法傳遞Context。
type logger interface {
Print(v ...interface{})
}
那么你想用常規(guī)方法把請求的traceId 記錄到GORM 生成的日志中是完全不可能實現(xiàn)的,只能借助一些非常規(guī)的方法,比如引入一個GLS開源庫,每個請求唯一的traceid、spanid 這些都放到gls里,記日志的時候再從GLS里把這些信息拿出來記錄到日志中去。
一般都不推薦引入GLS,實際上性能影響不明顯,之前有些服務(wù)請求量最大2000QPS的時候也沒出現(xiàn)過瓶頸。不過現(xiàn)在Github上star最多的那個GLS庫在 Go 1.20 版本以后已經(jīng)不能用了,不然我們之前公司的那些項目用的Go版本就不會卡在1.19啦
笑死╮(╯▽╰)╭
在GORM V2 中它新增了以下Logger 接口:
type Interface interface {
LogMode(LogLevel) Interface
Info(context.Context, string, ...interface{})
Warn(context.Context, string, ...interface{})
Error(context.Context, string, ...interface{})
Trace(ctx context.Context, begin time.Time, fc func() (sql string, rowsAffected int64), err error)
}
每個方法都有應(yīng)用的上下文Context參數(shù)傳遞進來,還專門提供了Trace方法讓我們實現(xiàn),供我們實現(xiàn)查詢的SQL語句和消耗時間的記錄。
當我們自己實現(xiàn)好GORM的Logger后,在GORM創(chuàng)建連接的時候需要把Logger選項配置成我們自定義Logger
db, err := gorm.Open(
mysql.Open(
cfg.Dsn, &gorm.Config{
Logger: MyGormLogger
})
)
在使用GORM執(zhí)行查詢的地方,通過withContext 帶上Context 信息即可
func (dao *userDao) AddUser(c context.Context, user *model.User) (userId int64, err error) {
err = db.GetConn().WithContext(c).Create(user).Error
....
}
關(guān)于怎么自定義實現(xiàn)GORM Logger 把GORM 日志統(tǒng)一整合到項目應(yīng)用日志,未來等我項目成型了再跟大家分享。接下來說下第二個讓我決定使用GORM V2 的原因
CREATE方法支持批量創(chuàng)建模型
在GORM V1版本里,模型本身是不在帶批量創(chuàng)建的功能的,想要批量創(chuàng)建一種選擇是寫個循環(huán),在循環(huán)里調(diào)用模型的Create方法。
還有一種是使用db.Raw 或者 db.Exec 執(zhí)行手寫的SQL來進行批量創(chuàng)建,我以前每次需要批量創(chuàng)建模型是都會手動在模型里定義一個BulkCreate方法
func BulkInsertOrderGoods(unsavedRows []*table.OrderGoods) error {
valueStrings := make([]string, 0, len(unsavedRows))
valueArgs := make([]interface{}, 0, len(unsavedRows)*3)
for _, row := range unsavedRows {
valueStrings = append(valueStrings, "(?, ?, ?)")
valueArgs = append(valueArgs, row.UserId)
valueArgs = append(valueArgs, row.GoodsName)
valueArgs = append(valueArgs, row.OrderId)
}
statement := fmt.Sprintf("INSERT INTO "+table.OrderGoods{}.TableName()+" (user_id, goods_name, order_id) VALUES %s",
strings.Join(valueStrings, ","))
err := DB().Exec(statement, valueArgs...).Error
return err
}
還得時刻注意,盡量讓程序拼接SQL時不出錯。
那么在GORM V2 里,我們只需要把模型對象的把模型切片傳給模型的Create方法
var users = []User{{Name: "jinzhu1"}, {Name: "jinzhu2"}, {Name: "jinzhu3"}}
db.Create(&users)
for _, user := range users {
user.ID // 1,2,3
}
或者是使用gorm.DB對象上的方法 CreateInBatches 來指定批量插入的批次大小
var users = []User{{Name: "jinzhu_1"}, ...., {Name: "jinzhu_10000"}}
// batch size 100
db.CreateInBatches(users, 100)
CreateInBatches方法需要在初始化GORM的時候指定對應(yīng)的配置,推薦還是用第一種方法。另外更新或者插入方法Upsert 在V2也支持批量操作。
我覺得有了這兩個特性,在新搭建項目的時候很難不選擇使用V2版本,第一個特性讓用日志排查問題變得更簡單,第二個特性能讓不用再去自己寫代碼實現(xiàn)批量操作。
接下來說幾個破壞性更新,這個可能是從V1 升級到 V2的障礙
需要注意的幾個破壞性更新
初始化方式變更
GORM V1 和 V2 用到的初始化Open方法發(fā)生了變更
/ jinzhu
func Open(dialect string, args ...interface{}) (db *DB, err error) {}
// grom.io
func Open(dialector Dialector, opts ...Option) (db *DB, err error) {}
此外還有一些設(shè)置連接的方式也有微調(diào),我把V1和V2 初始化的Demo 放在這里大家可以比較一下,首先是V1版本的
db, err := gorm.Open(config.Database.Type, config.Database.DSN)
if err != nil {
panic(err)
}
db.DB().SetMaxOpenConns(config.Database.MaxOpenConn)
db.DB().SetMaxIdleConns(config.Database.MaxIdleConn)
db.DB().SetConnMaxLifetime(config.Database.MaxLifeTime)
下面是V2版本的
db, err := gorm.Open(
mysql.Open(option.DSN),
&gorm.Config{
Logger: NewGormLogger(),
},
)
if err != nil {
panic(err)
}
sqlDb, _ := db.DB()
sqlDb.SetMaxOpenConns(option.MaxOpenConn)
sqlDb.SetMaxIdleConns(option.MaxIdleConn)
sqlDb.SetConnMaxLifetime(option.MaxLifeTime)
Find 查不到數(shù)據(jù)時不再返回Error
使用Find查詢數(shù)據(jù)的時候,在V1版本里如果查不到數(shù)據(jù)會返回錯誤,所以很多人在代碼里的下面這行判斷會失效
if err != gorm.ErrRecordNotFound
但是使用 First、Last、Take 這些預(yù)期會返回結(jié)果的方法查詢記錄時,還會返回 ErrRecordNotFound。
軟刪除支持更多模式
說到這里,發(fā)現(xiàn)有個很好的特性在上面忘記說了,汗。。。那就在這里在補充一下吧,GORM自帶的軟刪除我之前是不會用的,因為它那個字段名還有字段的默認值都是限定不能改的,默認值NULL,這在很多公司里DBA設(shè)置的約束里是不允許的。
所以我之前沒有使用過。但是現(xiàn)在GORM V2 支持Flag 模式了,就是咱們很多人用的0代表未刪除 1代表刪除
使用前需要先安裝GORM的soft_delete這個包。
go get -u "gorm.io/plugin/soft_delete"
在定義模型時像下面這樣給標記軟刪除的字段加上這個tag
type User struct {
ID uint
Name string
IsDel soft_delete.DeletedAt `gorm:"softDelete:flag"`
}
那么這樣GORM在執(zhí)行SQL語句時就會自動帶上is_del這個字段進行查詢啦
// Query
SELECT * FROM users WHERE is_del = 0;
// Delete
UPDATE users SET is_del = 1 WHERE ID = 1;
這個我覺得也很好用。
是否要升級V2
這里說的這些知識我目前體驗下來的幾點明顯變化,像什么數(shù)據(jù)遷移之類的我就沒看,因為真正做項目的時候沒這個權(quán)限。
大家覺得有必要從V1升級到V2嗎,反正我負責的這些祖?zhèn)骼享椖课沂遣桓覄拥?,新項目倒是可以無腦選擇V2。
咱們有踩過V1升V2版本的坑么,可以在評論區(qū)里說說呀。
最后在結(jié)尾推薦一下我研發(fā)的畫圖課,最近在更新 從零開始學(xué)會畫系統(tǒng)架構(gòu)圖 ,手把手帶你用公式拆解系統(tǒng)、畫架構(gòu)圖,專欄正在更新中,涵蓋了多個程序員內(nèi)功的提升,最近更是在籌劃與圖解領(lǐng)域的大博主和UP主加更怎么給技術(shù)博客畫圖的教程。希望大家多多訂閱支持呀!
訂閱方式一:公眾號專欄《程序員的全能畫圖課》
訂閱方式二:掃描下方二維碼或者訪問https://xiaobot.net/p/dev_img 訂閱
