前言:
本文介绍如何通过GormScopes来构造通用的分页查询。
问题引入
在使用Gorm查询数据的时候遇到分页查询是避免不了的,而分页查询也是一个通用的查询结构,为了避免大量的代码冗余,可以使用Scopes()来构造通用函数来解决这个问题。
正文
1.使用对比
首先我们看一张对比图,来观察一下分页代码构造的查询语句
引入前

引入后

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
| type Page struct { Page int PageSize int Total int64 Pages int64 }
func NewPage(page int, pageSize int) *Page { return &Page{ Page: page, PageSize: pageSize, } }
func (p *Page) Paginate(table interface{}) func(*gorm.DB) *gorm.DB { return func(d *gorm.DB) *gorm.DB { 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 }
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
| func (p *Page) Paginate(table interface{}) func(*gorm.DB) *gorm.DB { return func(d *gorm.DB) *gorm.DB { d.Model(table).Count(&p.Total) ……………… 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
| func (p *Page) Paginate(table interface{}) func(*gorm.DB) *gorm.DB { return func(d *gorm.DB) *gorm.DB { countDb := d.Session(&gorm.Session{NewDB: true}) countDb.Model(table).Count(&p.Total) ……………… 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 } }
|