GormScopes通用分页构造

前言:

本文介绍如何通过GormScopes来构造通用的分页查询。

问题引入

在使用Gorm查询数据的时候遇到分页查询是避免不了的,而分页查询也是一个通用的查询结构,为了避免大量的代码冗余,可以使用Scopes()来构造通用函数来解决这个问题。

正文

1.使用对比

首先我们看一张对比图,来观察一下分页代码构造的查询语句

引入前

image-20230731122208475

引入后

image-20230731122531313

Paginate内部构造

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
// Page 分页查询 辅助结构
type Page struct {
Page int // 当前页
PageSize int // 单页数量
Total int64 // 数据总量
Pages int64 // 总页数
}

func NewPage(page int, pageSize int) *Page {
return &Page{
Page: page,
PageSize: pageSize,
}
}

// Paginate 分页
func (p *Page) Paginate(table interface{}) func(*gorm.DB) *gorm.DB {
return func(d *gorm.DB) *gorm.DB {
// TODO 后续添加匹配规则,目前是每次查询都会下发总数量和页数计算。
// 拼接Count
countDb := d.Session(&gorm.Session{NewDB: true})
countDb.Model(table).Count(&p.Total)

// 过滤 当前页、单页数量; 计算总页数
if p.Page < 1 {
p.Page = 1
}

// 每页数量过滤
switch {
case p.PageSize > 100:
p.PageSize = enum.MaxPageSize
case p.PageSize <= 0:
p.PageSize = enum.MinPageSize
}

// 计算总页数 Total / PageSize = Pages
p.Pages = p.Total / int64(p.PageSize)
// 如果还有余那么也可以查询
if p.Total%int64(p.PageSize) != 0 {
p.Pages++
}
// 拼接分页
d.Offset(p.offset()).Limit(p.PageSize)
return d
}
}

func (p *Page) offset() int {
return (p.Page - 1) * p.PageSize
}

2.注意点(防踩坑)

Scopes() 本质上是一个回调函数,通过Scopes()可以组合封装通用的代码段,但是在使用的时候需要注意,Scopes()内部不能直接封装单独的查询语句,否则会造成扫描器检测数量和外部赋值结构不匹配!如果需要使用,请构造一个新的Session进行查询,例如我上面的例子中的countDb 就是构造了一个新的查询链接。

🤣 我在刚开始使用的时候一直扫描器报错,打断点后才发现是姿势不对,大家注意。

错误示范

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// Paginate 分页
func (p *Page) Paginate(table interface{}) func(*gorm.DB) *gorm.DB {
return func(d *gorm.DB) *gorm.DB {
// 直接使用回调的 d
d.Model(table).Count(&p.Total)
………………

// 计算总页数 Total / PageSize = Pages
p.Pages = p.Total / int64(p.PageSize)
// 如果还有余那么也可以查询
if p.Total%int64(p.PageSize) != 0 {
p.Pages++
}
// 拼接分页
d.Offset(p.offset()).Limit(p.PageSize)
return d
}
}

导致结果就是.Count(&p.Total )和 外层的Find(comments) 结构不同,扫描器检测不合法直接拒绝本次查询。

  • 正确姿势
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Paginate 分页
func (p *Page) Paginate(table interface{}) func(*gorm.DB) *gorm.DB {
return func(d *gorm.DB) *gorm.DB {
// 使用新的Session单独查询
countDb := d.Session(&gorm.Session{NewDB: true})
countDb.Model(table).Count(&p.Total)

………………

// 计算总页数 Total / PageSize = Pages
p.Pages = p.Total / int64(p.PageSize)
// 如果还有余那么也可以查询
if p.Total%int64(p.PageSize) != 0 {
p.Pages++
}
// 拼接分页
d.Offset(p.offset()).Limit(p.PageSize)
return d
}
}

GormScopes通用分页构造
http://yoursite.com/2023/07/31/GormScopes通用分页构造/
作者
Meng-Xin
发布于
2023年7月31日
许可协议