本文重點
- 使用goframe v2最新版的最佳實踐
- 列表取值slice容量初始化,避免scan動態(tài)擴容
- slice的延遲初始化
- 更新操作注意的問題
老規(guī)則:我把詳細步驟已經(jīng)整理好,大家可以參考這個步驟進行開發(fā),更歡迎提優(yōu)化建議。
取值列表優(yōu)化
下方代碼示例是項目之前的列表取值寫法,和官方示例focus-single寫法一樣,思路如下:
- 獲得*gdb.Model對象,方便后續(xù)調用
- 實例化返回結構體
- 分頁查詢
- 執(zhí)行查詢和賦值(只是為了查詢有無數(shù)據(jù),并沒有賦值到響應結構體中)
- 無數(shù)據(jù)判斷
- 再查詢count,獲得數(shù)據(jù)個數(shù)
- 把查詢到的結果賦值到響應結構體中
每段代碼都寫清楚了注釋,這么寫能實現(xiàn)功能,但是性能不夠好,還有優(yōu)化空間:
// GetList 查詢內容列表
func (s *sAdmin) GetList(ctx context.Context, in model.AdminGetListInput) (out *model.AdminGetListOutput, err error) {
//1.獲得*gdb.Model對象,方便后續(xù)調用
var (
m = dao.AdminInfo.Ctx(ctx)
)
//2. 實例化返回結構體
out = &model.AdminGetListOutput{
Page: in.Page,
Size: in.Size,
}
//3. 分頁查詢
listModel := m.Page(in.Page, in.Size)
//4. 執(zhí)行查詢和賦值(只是為了查詢有無數(shù)據(jù),并沒有賦值到響應結構體中)
var list []*entity.AdminInfo
if err := listModel.Scan(&list); err != nil {
return out, err
}
//5.無數(shù)據(jù)判斷
if len(list) == 0 {
return out, nil
}
//6. 再查詢count,獲得數(shù)據(jù)個數(shù)
out.Total, err = m.Count()
if err != nil {
return out, err
}
//7. 把查詢到的結果賦值到響應結構體中
if err := listModel.Scan(&out.List); err != nil {
return out, err
}
return
}
整理一下上面代碼的問題:
- 步驟4沒有必要,可以直接查詢count,如果count為0直接返回;否則再執(zhí)行查詢賦值操作
- 上述這種寫法有個問題:當沒有查詢到數(shù)據(jù)時,list值為null,但是我們期望的返回值為空數(shù)組[]

- 還有就是slice的容量初始化下會更好,scan期間不會有擴容行為
- 再者就是延遲slice的初始化,如果前面出錯,就沒有必要實例化列表了
我們優(yōu)化一下代碼,優(yōu)化后的代碼如下,也寫了詳細的注釋:
- 獲得*gdb.Model對象,方便后續(xù)調用
- 實例化響應結構體
- 分頁查詢
- 再查詢count,判斷有無數(shù)據(jù)
- 延遲初始化list切片 確定有數(shù)據(jù),再按期望大小初始化切片容量
- 把查詢到的結果賦值到響應結構體中
// GetList 查詢內容列表
func (s *sAdmin) GetList(ctx context.Context, in model.AdminGetListInput) (out *model.AdminGetListOutput, err error) {
//1. 獲得*gdb.Model對象,方便后續(xù)調用
m := dao.AdminInfo.Ctx(ctx)
//2. 實例化響應結構體
out = &model.AdminGetListOutput{
Page: in.Page,
Size: in.Size,
}
//3. 分頁查詢
listModel := m.Page(in.Page, in.Size)
//4. 再查詢count,判斷有無數(shù)據(jù)
out.Total, err = m.Count()
if err != nil || out.Total == 0 {
//解決空數(shù)據(jù)返回[] 而不是返回nil的問題
out.List = make([]model.AdminGetListOutputItem, 0, 0)
return out, err
}
//5. 延遲初始化list切片 確定有數(shù)據(jù),再按期望大小初始化切片容量
out.List = make([]model.AdminGetListOutputItem, 0, in.Size)
//6. 把查詢到的結果賦值到響應結構體中
if err := listModel.Scan(&out.List); err != nil {
return out, err
}
return
}
優(yōu)化代碼之后,無數(shù)據(jù)的list返回格式和預期一樣為[]:

這是有數(shù)據(jù)的返回結果示例:

以上優(yōu)化記錄已經(jīng)同步到GitHub,歡迎查看、復刻經(jīng)驗:
??https://github.com/wangzhongyang007/goframe-shop-v2/commit/ee020ea96616c30cb5bce5f7ab24417ad56e1a67??
上面的例子很簡單,就是普通的查詢數(shù)據(jù),也沒有搜索條件,也不涉及到模型關聯(lián)。
關聯(lián)查詢取值
咱們再舉一個復雜點的例子,帶大家進階一下,我就直接安排優(yōu)化后的代碼了:
- 定義全局通用的查詢語句
- 實例化響應結構體
- 翻頁查詢
- 優(yōu)先查詢count,報錯或者無數(shù)據(jù)則直接返回
- 延遲初始化list 確定有數(shù)據(jù)再按期望大小,實例化切片的容量
- 進一步優(yōu)化:根據(jù)傳入?yún)?shù)區(qū)分查詢對應的關聯(lián)模型
// GetList 查詢內容列表
func (*sCollection) GetList(ctx context.Context, in model.CollectionListInput) (out *model.CollectionListOutput, err error) {
//1. 定義全局通用的查詢語句
userId := gconv.Uint(ctx.Value(consts.CtxUserId))
m := dao.CollectionInfo.Ctx(ctx).Where(dao.CollectionInfo.Columns().Type, in.Type).
Where(dao.CollectionInfo.Columns().UserId, userId)
//2. 實例化響應結構體
out = &model.CollectionListOutput{
Page: in.Page,
Size: in.Size,
}
//3. 翻頁查詢
listModel := m.Page(in.Page, in.Size)
//4. 優(yōu)先查詢count,報錯或者無數(shù)據(jù)則直接返回
out.Total, err = listModel.Count()
if err != nil || out.Total == 0 {
out.List = make([]model.CollectionListOutputItem, 0, 0)
return out, err
}
//5. 延遲初始化list 確定有數(shù)據(jù)再按期望大小,實例化切片的容量
out.List = make([]model.CollectionListOutputItem, 0, in.Size)
//6. 進一步優(yōu)化:根據(jù)傳入?yún)?shù)區(qū)分查詢對應的關聯(lián)模型
if in.Type == consts.CollectionTypeGoods {
if err := listModel.With(model.GoodsItem{}).Scan(&out.List); err != nil {
return out, err
}
} else if in.Type == consts.CollectionTypeArticle {
if err := listModel.With(model.ArticleItem{}).Scan(&out.List); err != nil {
return out, err
}
} else {
if err := listModel.WithAll().Scan(&out.List); err != nil {
return out, err
}
}
return
}
上面的示例使用了with模型關聯(lián),核心思路是:根據(jù)傳入?yún)?shù)區(qū)分查詢對應的關聯(lián)模型,不做無效查詢。
更新操作
使用OmitEmpty,更新操作過濾空值,比如:
func (*sAddress) Update(ctx context.Context, in model.UpdateAddressInput) (err error) {
if _, err = dao.AddressInfo.Ctx(ctx).Data(in).OmitEmpty().Where(dao.AddressInfo.Columns().Id, in.Id).Update(); err != nil {
return err
}
return nil
}
我開發(fā)過程中原本沒有使用OmitEmpty(),忽略了這個問題,感謝這位朋友提的建議。
開源項目地址:
做開源項目這件事,從沒想過一蹴而就,想得一直是越來越好,投入長期精力:??https://github.com/wangzhongyang007/goframe-shop-v2??
本文轉載自微信公眾號「 程序員升級打怪之旅」,作者「王中陽Go」,可以通過以下二維碼關注。

轉載本文請聯(lián)系「 程序員升級打怪之旅」公眾號。