王中阳Go

V1

2023/03/21阅读:25主题:萌绿

goframe优化方案to小严

取值列表优化

这是我们项目中比较典型的取值列表写法,和官方示例focus-single写法一样,思路如下:

  1. 获得*gdb.Model对象,方面后续调用
  2. 实例化返回结构体
  3. 分页查询
  4. 执行查询和赋值(只是为了查询有无数据,并没有赋值到响应结构体中)
  5. 无数据判断
  6. 再查询count,获得数据个数
  7. 把查询到的结果赋值到响应结构体中

每段代码都写清楚了注释,这么写能实现功能,但是性能不够好,还有优化空间:

// GetList 查询内容列表
func (s *sAdmin) GetList(ctx context.Context, in model.AdminGetListInput) (out *model.AdminGetListOutput, err error) {
 //1.获得*gdb.Model对象,方面后续调用
 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. 执行查询和赋值(只是为了查询有无数据,并没有赋值到响应结构体中)
 var list []*entity.AdminInfo
 if err := listModel.Scan(&list); err != nil {
  return out, err
 }
 //5.无数据判断
 if len(list) == 0 {
  return out, nil
 }
 //6. 再查询count,获得数据个数
 out.Total, err = m.Count()
 if err != nil {
  return out, err
 }
 //7. 把查询到的结果赋值到响应结构体中
 if err := listModel.Scan(&out.List); err != nil {
  return out, err
 }
 return
}

咱们整理一下上面代码的问题:

  1. 步骤4没有必要,可以直接查询count,如果count为0直接返回;否则再执行查询赋值操作

  2. 上述这种写法有个问题:当没有查询到数据时,list值为null,但是我们期望的返回值为空数组[]

  3. 还有就是slice的容量初始化下会更好,scan期间不会有扩容行为

  4. 再者就是延迟slice的初始化,如果前面出错,就没有必要实例化列表了

我们优化一下代码,优化后的代码如下,也写了详细的注释:

  1. 获得*gdb.Model对象,方面后续调用
  2. 实例化响应结构体
  3. 分页查询
  4. 再查询count,判断有无数据
  5. 延迟初始化list切片 确定有数据,再按期望大小初始化切片容量
  6. 把查询到的结果赋值到响应结构体中
// GetList 查询内容列表
func (s *sAdmin) GetList(ctx context.Context, in model.AdminGetListInput) (out *model.AdminGetListOutput, err error) {
 //1. 获得*gdb.Model对象,方面后续调用
 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,判断有无数据
 out.Total, err = m.Count()
 if err != nil || out.Total == 0 {
   //解决空数据返回[] 而不是返回nil的问题
  out.List = make([]model.AdminGetListOutputItem, 00)
  return out, err
 }
 //5. 延迟初始化list切片 确定有数据,再按期望大小初始化切片容量
 out.List = make([]model.AdminGetListOutputItem, 0, in.Size)
 //6. 把查询到的结果赋值到响应结构体中
 if err := listModel.Scan(&out.List); err != nil {
  return out, err
 }
 return
}

优化代码之后,无数据的list返回格式和预期一样为[]:

这是有数据的返回结果示例:

以上优化记录已经同步到GitHub,欢迎查看、复刻经验:

https://github.com/wangzhongyang007/goframe-shop-v2/commit/ee020ea96616c30cb5bce5f7ab24417ad56e1a67

上面的例子很简单,就是普通的查询数据,也没有搜索条件,也不涉及到模型关联。

关联查询取值

咱们再举一个复杂点的例子,带大家进阶一下,我就直接安排优化后的代码了:

  1. 定义全局通用的查询语句
  2. 实例化响应结构体
  3. 翻页查询
  4. 优先查询count,报错或者无数据则直接返回
  5. 延迟初始化list 确定有数据再按期望大小,实例化切片的容量
  6. 进一步优化:根据传入参数区分查询对应的关联模型
// 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. 优先查询count,报错或者无数据则直接返回
 out.Total, err = listModel.Count()
 if err != nil || out.Total == 0 {
  out.List = make([]model.CollectionListOutputItem, 00)
  return out, err
 }
 //5. 延迟初始化list 确定有数据再按期望大小,实例化切片的容量
 out.List = make([]model.CollectionListOutputItem, 0, in.Size)
 //6. 进一步优化:根据传入参数区分查询对应的关联模型
 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.With().Scan(&out.List); err != nil {
   return out, err
  }
 }
 return
}

分类:

后端

标签:

后端

作者介绍

王中阳Go
V1

专注Go语言的学习经验分享和简历优化