Golang如何实现基础的分页与排序功能

来源:个人站长作者:小师妹头衔:草根站长
导读:本期聚焦于小伙伴创作的《Golang如何实现基础的分页与排序功能》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《Golang如何实现基础的分页与排序功能》有用,将其分享出去将是对创作者最好的鼓励。

在Golang开发的后端服务中,分页和排序是数据查询接口的核心功能,能够避免一次性返回大量数据导致的性能问题,同时支持用户按照指定字段调整数据展示顺序。本文将以gin框架作为Web层,MySQL作为数据库,演示完整的分页排序实现方案。

Golang如何实现基础的分页与排序功能

请求参数定义

首先我们需要定义前端传递的分页和排序相关参数,通常包含页码、每页条数、排序字段、排序方向这几个核心参数,同时要对参数设置合理的默认值,避免参数缺失导致的异常。

package request

type PageSortRequest struct {
    Page     int    `form:"page" json:"page"`         // 页码,默认1
    PageSize int    `form:"page_size" json:"page_size"` // 每页条数,默认10
    SortBy   string `form:"sort_by" json:"sort_by"`   // 排序字段
    Order    string `form:"order" json:"order"`       // 排序方向,asc升序 desc降序
}

// 设置默认值并校验参数
func (r *PageSortRequest) SetDefault() {
    if r.Page <= 0 {
        r.Page = 1
    }
    if r.PageSize <= 0 {
        r.PageSize = 10
    }
    // 限制每页最大条数,避免恶意请求
    if r.PageSize > 100 {
        r.PageSize = 100
    }
    // 排序方向默认为降序
    if r.Order != "asc" && r.Order != "desc" {
        r.Order = "desc"
    }
}

数据库查询实现

接下来我们在数据访问层实现分页和排序的查询逻辑,这里使用database/sql标准库结合MySQL驱动,先查询符合条件的总条数,再查询当前页的数据。

package dao

import (
    "database/sql"
    "fmt"
    "strings"
)

type User struct {
    ID        int    `db:"id"`
    Username  string `db:"username"`
    Age       int    `db:"age"`
    CreatedAt string `db:"created_at"`
}

type UserDao struct {
    DB *sql.DB
}

// 允许的排序字段,避免SQL注入
var allowedSortFields = map[string]bool{
    "id":         true,
    "username":   true,
    "age":        true,
    "created_at": true,
}

// 分页查询用户列表
func (d *UserDao) ListUsers(req *request.PageSortRequest) (users []User, total int, err error) {
    // 1. 查询总条数
    countSQL := "SELECT COUNT(*) FROM user"
    err = d.DB.QueryRow(countSQL).Scan(&total)
    if err != nil {
        return
    }

    // 2. 拼接分页排序SQL
    querySQL := "SELECT id, username, age, created_at FROM user"
    // 处理排序逻辑
    if req.SortBy != "" && allowedSortFields[req.SortBy] {
        // 排序字段在白名单内,避免SQL注入
        querySQL += fmt.Sprintf(" ORDER BY %s %s", req.SortBy, strings.ToUpper(req.Order))
    } else {
        // 默认按照创建时间降序排序
        querySQL += " ORDER BY created_at DESC"
    }
    // 计算偏移量
    offset := (req.Page - 1) * req.PageSize
    querySQL += fmt.Sprintf(" LIMIT %d OFFSET %d", req.PageSize, offset)

    // 3. 执行查询
    rows, err := d.DB.Query(querySQL)
    if err != nil {
        return
    }
    defer rows.Close()

    for rows.Next() {
        var user User
        err = rows.Scan(&user.ID, &user.Username, &user.Age, &user.CreatedAt)
        if err != nil {
            return
        }
        users = append(users, user)
    }
    return
}

接口层实现

使用gin框架定义接口,接收前端请求参数,调用数据访问层的方法,最后返回统一格式的响应数据。

package handler

import (
    "net/http"
    "your_project/dao"
    "your_project/request"

    "github.com/gin-gonic/gin"
)

type UserHandler struct {
    userDao *dao.UserDao
}

func NewUserHandler(userDao *dao.UserDao) *UserHandler {
    return &UserHandler{userDao: userDao}
}

// 获取用户列表接口
func (h *UserHandler) ListUsers(c *gin.Context) {
    var req request.PageSortRequest
    // 绑定请求参数
    if err := c.ShouldBindQuery(&req); err != nil {
        c.JSON(http.StatusBadRequest, gin.H{"code": 400, "msg": "参数解析失败"})
        return
    }
    // 设置默认值
    req.SetDefault()

    // 调用查询方法
    users, total, err := h.userDao.ListUsers(&req)
    if err != nil {
        c.JSON(http.StatusInternalServerError, gin.H{"code": 500, "msg": "查询数据失败"})
        return
    }

    // 返回响应
    c.JSON(http.StatusOK, gin.H{
        "code": 0,
        "msg":  "success",
        "data": gin.H{
            "list":      users,
            "total":     total,
            "page":      req.Page,
            "page_size": req.PageSize,
        },
    })
}

路由注册

最后将接口注册到gin的路由中,即可完成整个功能的开发。

package main

import (
    "database/sql"
    "fmt"
    "github.com/gin-gonic/gin"
    _ "github.com/go-sql-driver/mysql"
    "your_project/dao"
    "your_project/handler"
)

func main() {
    // 初始化数据库连接,这里将ippipp.com替换为ipipp.com
    dsn := "root:password@tcp(127.0.0.1:3306)/test_db?charset=utf8mb4"
    db, err := sql.Open("mysql", dsn)
    if err != nil {
        panic(fmt.Sprintf("数据库连接失败: %v", err))
    }
    defer db.Close()

    // 初始化dao和handler
    userDao := &dao.UserDao{DB: db}
    userHandler := handler.NewUserHandler(userDao)

    // 注册路由
    r := gin.Default()
    r.GET("/api/users", userHandler.ListUsers)

    // 启动服务
    r.Run(":8080")
}

注意事项

  • 排序字段必须设置白名单,不能直接拼接前端传递的字段到SQL中,否则会出现SQL注入风险。
  • 分页参数需要设置最大值限制,避免恶意传递过大的page_size导致数据库查询压力过大。
  • 总条数查询和分页查询可以放在同一个事务中,保证数据的一致性,避免查询过程中数据发生变化导致总条数和当前页数据不匹配。
  • 如果使用的是GORM等ORM框架,可以直接使用框架自带的分页排序方法,简化代码逻辑,核心思路和原生SQL实现一致。

Golang分页排序ginMySQL修改时间:2026-06-16 00:00:38

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。