★ 分页查询:Limit/Page 与 AllAndCount/ScanAndCount

面试问法:GoFrame 分页列表怎么做?怎么同时拿到列表和总数? 一句话结论:ORM 提供 Limit(offset, size)Page(page, size) 两种分页;官方从 v2.5 起提供 AllAndCount/ScanAndCount,一条链同时完成“分页查询 + COUNT”,省去先 Count 再查列表的样板。配套规范路由在 Req/Res 里定义 page/size 与 list/total 即可。

方法速查

方法作用说明
Limit(offset, size)经典偏移分页需自己算 offset
Page(page, size)页码分页内部换算 offset
Count()只数总数老写法要另起一条链
AllAndCount()查列表 Result + 总数v2.5+,返回 (result, total, err)
ScanAndCount(&list,&total,flag)扫描进结构体 + 总数v2.5+,列表与总数一步到位

规范路由下的标准写法

type UserListReq struct {
    g.Meta `path:"/user" method:"get" tags:"User" summary:"user list"`
    Page   int `d:"1"  v:"min:1"          dc:"页码"`
    Size   int `d:"10" v:"between:1,100"  dc:"每页数量"`
    Status int `dc:"状态过滤,0 不过滤"`
}
type UserListRes struct {
    List  []*entity.User `json:"list"`
    Total int            `json:"total"`
    Page  int            `json:"page"`
    Size  int            `json:"size"`
}
func List(ctx context.Context, in *v1.UserListReq) (out *v1.UserListRes, err error) {
    m := dao.User.Ctx(ctx)
    if in.Status > 0 {
        m = m.Where("status", in.Status)
    }
    out = &v1.UserListRes{Page: in.Page, Size: in.Size}
    // 第三参 false:总数用 COUNT(1);true 则按指定字段 count
    err = m.Page(in.Page, in.Size).OrderDesc("id").ScanAndCount(&out.List, &out.Total, false)
    return
}

老版本/特殊场景等价写法:先 total, err := m.Count(),再 m.Page(...).OrderDesc("id").Scan(&out.List)

注意点

  • Count 不受 Limit/Page 影响,这是 ScanAndCount/AllAndCount 的既定行为。
  • 带 GROUP BY/子查询时 count 可能不等于行数,需单独设计计数逻辑。
  • 服务端封顶 size(如 between:1,100),防止超大分页扫全表。
  • 排序字段做白名单:自定义排序别把任意列名拼进 Order,默认主键倒序最稳。

常见追问

  • 追问:深分页性能差怎么办?——offset 越大扫描越多;大结果集改用游标/键集分页(where id < 游标值 order by id desc limit size)。
  • 追问:ScanAndCount 会比两条查询更快吗?——不会减少次数(仍是一次列表 + 一次 COUNT),省的是样板与出错概率,优化点应放在索引与避免 N+1。

记忆点

  • 一条链收尾:Page + OrderDesc + ScanAndCount(&list,&total,false);语义与版本要求以官方文档为准。
笔记加载中…