Gorm是Go语言中最常用的ORM(对象关系映射)包之一,但它在某些功能上仍有不足,其中之一就是分页。分页是管理Web应用程序中大数据集的基本功能。通过分页,可以限制和显示数据库中的部分数据,而不必一次性检索整个表的数据。
虽然Gorm的文档中介绍了如何使用Scopes来实现分页,但在灵活性和可用性上仍有改进空间。本文介绍了一种利用Gorm的Clauses特性来简化分页并扩展其功能的优雅替代方案。
使用Gorm的Scopes进行分页
Gorm的文档中将Scopes介绍为重用常用代码的方法。在文档示例中,我们可以看到定义了一个类似于以下的分页Scope函数:
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。这种方法与使用Scopes略有不同,因为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子句函数所需的两个接口:
func (p *Pagination) ModifyStatement(stm *gorm.Statement) {
// 修改语句以添加分页
db := stm.DB
stm.DB.Limit(p.pageSize).Offset((p.page - 1) * p.pageSize)
}
func (p *Pagination) Build(_ clause.Builder) {
// Build方法留空,因为分页不需要额外的SQL子句
}
之后,可以按如下方式使用分页:
pagination := Pagination{
page: 0,
pageSize: 10,
}
db.Clauses(&pagination).Find(&users)
为了使这种方法可重用并增强其功能,我开发了Pagorminator——一个简化Gorm分页并添加高级功能的库,例如未分页请求和自动元数据填充(如总页数和总计数)。使用方法如下:
// 添加插件
_ = db.Use(pagorminator.PaGormMinator{})
pageRequest, _ := pagorminator.PageRequest(0, 10)
db.Clauses(pageRequest).Find(&users)
// 这将应用分页,并填充pageRequest,包括:
// - 总页数
// - 总计数
分页是数据库驱动应用程序的关键功能。通过利用Clauses和类似Pagorminator的工具,可以在Gorm中实现强大且可重用的分页功能。