GORM 是 Go 中使用最广泛的 ORM 包,但尽管如此,它缺少一些“基本”功能。其中一个缺失的功能就是分页(Pagination)。
分页是管理应用程序中大型数据集的一个重要功能。它是一种限制和显示数据库中部分总数据的方法,这样就不需要一次性检索整个表,这样可以极大的提高接口性能,降低超时失败的概率。
虽然 GORM 提供了关于如何使用scopes 进行分页的文档,但在灵活性和可用性方面仍有改进的空间。在这里,我将介绍一种使用 GORM 的 Clauses 进行分页的替代方法。
使用 Scopes 的分页
GORM 的官网文档介绍了使用 Scopes 进行分页的代码示例:
func Paginate(page, pageSize int) func(db *gorm.DB) *gorm.DB {
return func (db *gorm.DB) *gorm.DB {
// validate page and pageSize
...
offset := (page - 1) * pageSize
return db.Offset(offset).Limit(pageSize)
}
}
page := 0
pageSize := 10
db.Scopes(Paginate(page, pageSize)).Find(&users)
因此,为了使用分页功能,我们需要使用以下代码 db.Scopes(Paginate(page, pageSize))…
使用 Clauses 的分页
一种不同且可以说更优雅的方法是使用 GORM Clauses,这种方法与使用 scope 略有不同,因为 Clauses 负责修改数据库查询,特别是 WHERE 子句。
让我们先定义分页结构体:
type Pagination struct {
page int
pageSize int
}
func (p *Pagination) GetPage() int {
return p.page
}
func (p *Pagination) GetPageSize() int {
return p.pageSize
}
然后,让我们实现两个接口,以便在 Gorm Clauses 函数中使用这个结构体:
clause.Expression
gorm.StatementModifier
所以结构看起来是这样的:
func (p *Pagination) ModifyStatement(stm *gorm.Statement) {
// We modify statement to add the pagination
db := stm.DB
stm.DB.Limit(p.size).Offset((p.page - 1) * p.pageSize)
}
func (p *Pagination) Build(_ clause.Builder) {
// The Build method is left empty since pagination does not require any additional SQL clauses.
}
之后,可以按照以下方式使用分页:
pagination := Pagination{
page: 0,
pageSize: 10,
}
db.Clauses(&pagination).Find(&users)
为了使这种方法可重用并增强其功能,这里介绍一个小众的包 PaGorminator:一个利用此功能进行分页的库,同时添加了其他功能,如无分页请求和自动填充元数据,如总页数和总计数。
使用方法如下:
// Add plugin
_ = db.Use(pagorminator.PaGormMinator{})
...
pageRequest, _ := pagorminator.PageRequest(0, 10)
db.Clauses(pageRequest).Find(&users)
// this will apply pagination, and also populate pageRequest with:
// - the total number of pages
// - total count
往期文章推荐
欢迎关注我:
- 知识星球:云原生 AI 实战营,10+ 高质量体系课( Go、云原生、AI Infra)、15+ 实战项目,助你提高技术天花板,入大厂、拿高薪;
- 微信GZH:令飞编程,分享 Go、云原生、AI Infra 相关技术。回复「资料」免费下载 Go、云原生、AI 等学习资料;
- 哔哩哔哩:令飞编程 ,分享技术、职场、面经等,并有免费直播课「云原生AI高新就业课」,大厂级项目实战到大厂面试通关。