可能这就是做开源项目的意义吧!

开源
使用了with模型关联,核心思路是:根据传入参数区分查询对应的关联模型,不做无效查询。

本文重点

  1. 使用goframe v2最新版的最佳实践
  2. 列表取值slice容量初始化,避免scan动态扩容
  3. slice的延迟初始化
  4. 更新操作注意的问题

老规则:我把详细步骤已经整理好,大家可以参考这个步骤进行开发,更欢迎提优化建议。

取值列表优化

下方代码示例是项目之前的列表取值写法,和官方示例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,但是我们期望的返回值为空数组[]

图片

  1. 还有就是slice的容量初始化下会更好,scan期间不会有扩容行为
  2. 再者就是延迟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, 0, 0)
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, 0, 0)
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.WithAll().Scan(&out.List); err != nil {
return out, err
}
}
return
}

上面的示例使用了with模型关联,核心思路是:根据传入参数区分查询对应的关联模型,不做无效查询。

更新操作

使用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
}

我开发过程中原本没有使用OmitEmpty(),忽略了这个问题,感谢这位朋友提的建议。

开源项目地址:

做开源项目这件事,从没想过一蹴而就,想得一直是越来越好,投入长期精力:​https://github.com/wangzhongyang007/goframe-shop-v2​

本文转载自微信公众号「 程序员升级打怪之旅」,作者「王中阳Go」,可以通过以下二维码关注。

转载本文请联系「 程序员升级打怪之旅」公众号。

责任编辑:武晓燕 来源: 程序员升职加薪之旅
相关推荐

2015-08-26 17:02:45

2018-11-08 15:30:04

JavaScriptES6异步

2015-07-21 10:24:02

Windows RT升级

2014-01-02 14:04:42

2014-04-14 09:58:18

开源项目

2019-01-02 04:40:19

物联网企业IOT

2021-09-03 10:44:42

ThreadLocalObject 数组

2020-02-17 15:55:22

Office 365

2024-12-13 16:37:56

SpringBootJava

2014-03-19 10:26:03

持续更新软件开发

2016-01-12 17:01:45

Bootstrap原因

2024-04-24 09:47:36

2015-07-27 10:56:02

2020-07-17 19:31:19

PythonR编程

2015-01-09 10:10:00

Linux

2018-11-01 13:38:51

Java中断停止

2013-08-14 14:36:07

开源项目

2021-02-20 17:36:30

Google开源项目漏洞

2012-03-06 09:17:11

开源项目运作

2022-07-27 14:47:01

开源项目
点赞
收藏

51CTO技术栈公众号