Golang如何用gorm实现分页的功能

作者:

文章目录
  • 在提供列表接口时一般要用到分页,对于存储在某些数据库中的数据进行分页起来非常的方便,下文给出一个通过gorm进行分页并通过http返回数据的例子。
  • gorm官方文档地址:https://gorm.io/zh_CN/docs/,库下载如下: go get -u gorm.io/gorm 本文使用的数据库是mysql,因此要下载mysql驱动: go get -u gorm.io/driver/mysql 这里使用gin框架来提供查询列表的接口想,库下载如下: go get -u github.com/gin-gonic/gin
  • 分页的方法有很多,上面只是给出了其中一种,根据自己项目的业务场景选择一种最喜欢的方法就行。 以上为个人经验,希望能给大家一个参考,也希望大家多多支持风君子博客。 您可能感兴趣的文章: Golang开发使用gorm时打印SQL语句方式 Golang Gorm实现自定义多态模型关联查询 Golang使用gorm实现分页功能的示例代码 Golang创建第一个web项目(Gin+Gorm) Golang实现自定义时间结构体并支持Json&Gorm
  • 目录
    • 背景
    • go库下载
    • 初始化数据
      • 【1】建表
      • 【2】插入数据
      • 【3】查看数据
    • 4、代码示例
      • 【1】gorm结构体定义
      • 【2】分页结构体定义
      • 【3】封装分页方法
      • 【4】封装获取数据库连接方法
      • 【5】查询列表接口
      • 【6】启动http服务
      • 【7】调用获取列表接口
    • 总结

      在提供列表接口时一般要用到分页,对于存储在某些数据库中的数据进行分页起来非常的方便,下文给出一个通过gorm进行分页并通过http返回数据的例子。

      gorm官方文档地址:https://gorm.io/zh_CN/docs/,库下载如下:

      go get -u gorm.io/gorm

      本文使用的数据库是mysql,因此要下载mysql驱动:

      go get -u gorm.io/driver/mysql

      这里使用gin框架来提供查询列表的接口想,库下载如下:

      go get -u github.com/gin-gonic/gin

      建一个用来测试分页的表,sql如下:

      CREATE TABLE test_page
      (
          id          INT AUTO_INCREMENT PRIMARY KEY,
          number      INT NOT NULL,
          create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
          update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
      ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COMMENT '测试分页表';
      

      插入30条numbercreate_time字段不同的数据,sql如下:

      INSERT INTO test_page (number, create_time)
      VALUES (1, NOW() + INTERVAL 0 SECOND),
             (2, NOW() + INTERVAL 1 SECOND),
             (3, NOW() + INTERVAL 2 SECOND),
             (4, NOW() + INTERVAL 3 SECOND),
             (5, NOW() + INTERVAL 4 SECOND),
             (6, NOW() + INTERVAL 5 SECOND),
             (7, NOW() + INTERVAL 6 SECOND),
             (8, NOW() + INTERVAL 7 SECOND),
             (9, NOW() + INTERVAL 8 SECOND),
             (10, NOW() + INTERVAL 9 SECOND),
             (11, NOW() + INTERVAL 10 SECOND),
             (12, NOW() + INTERVAL 11 SECOND),
             (13, NOW() + INTERVAL 12 SECOND),
             (14, NOW() + INTERVAL 13 SECOND),
             (15, NOW() + INTERVAL 14 SECOND),
             (16, NOW() + INTERVAL 15 SECOND),
             (17, NOW() + INTERVAL 16 SECOND),
             (18, NOW() + INTERVAL 17 SECOND),
             (19, NOW() + INTERVAL 18 SECOND),
             (20, NOW() + INTERVAL 19 SECOND),
             (21, NOW() + INTERVAL 20 SECOND),
             (22, NOW() + INTERVAL 21 SECOND),
             (23, NOW() + INTERVAL 22 SECOND),
             (24, NOW() + INTERVAL 23 SECOND),
             (25, NOW() + INTERVAL 24 SECOND),
             (26, NOW() + INTERVAL 25 SECOND),
             (27, NOW() + INTERVAL 26 SECOND),
             (28, NOW() + INTERVAL 27 SECOND),
             (29, NOW() + INTERVAL 28 SECOND),
             (30, NOW() + INTERVAL 29 SECOND);
      

      查看创建出来的30条数据:

      mysql [xxx]> select * from test_page;
      +----+--------+---------------------+---------------------+
      | id | number | create_time         | update_time         |
      +----+--------+---------------------+---------------------+
      |  1 |      1 | 2024-10-21 07:11:19 | 2024-10-21 07:11:19 |
      |  2 |      2 | 2024-10-21 07:11:20 | 2024-10-21 07:11:19 |
      |  3 |      3 | 2024-10-21 07:11:21 | 2024-10-21 07:11:19 |
      |  4 |      4 | 2024-10-21 07:11:22 | 2024-10-21 07:11:19 |
      |  5 |      5 | 2024-10-21 07:11:23 | 2024-10-21 07:11:19 |
      |  6 |      6 | 2024-10-21 07:11:24 | 2024-10-21 07:11:19 |
      |  7 |      7 | 2024-10-21 07:11:25 | 2024-10-21 07:11:19 |
      |  8 |      8 | 2024-10-21 07:11:26 | 2024-10-21 07:11:19 |
      |  9 |      9 | 2024-10-21 07:11:27 | 2024-10-21 07:11:19 |
      | 10 |     10 | 2024-10-21 07:11:28 | 2024-10-21 07:11:19 |
      | 11 |     11 | 2024-10-21 07:11:29 | 2024-10-21 07:11:19 |
      | 12 |     12 | 2024-10-21 07:11:30 | 2024-10-21 07:11:19 |
      | 13 |     13 | 2024-10-21 07:11:31 | 2024-10-21 07:11:19 |
      | 14 |     14 | 2024-10-21 07:11:32 | 2024-10-21 07:11:19 |
      | 15 |     15 | 2024-10-21 07:11:33 | 2024-10-21 07:11:19 |
      | 16 |     16 | 2024-10-21 07:11:34 | 2024-10-21 07:11:19 |
      | 17 |     17 | 2024-10-21 07:11:35 | 2024-10-21 07:11:19 |
      | 18 |     18 | 2024-10-21 07:11:36 | 2024-10-21 07:11:19 |
      | 19 |     19 | 2024-10-21 07:11:37 | 2024-10-21 07:11:19 |
      | 20 |     20 | 2024-10-21 07:11:38 | 2024-10-21 07:11:19 |
      | 21 |     21 | 2024-10-21 07:11:39 | 2024-10-21 07:11:19 |
      | 22 |     22 | 2024-10-21 07:11:40 | 2024-10-21 07:11:19 |
      | 23 |     23 | 2024-10-21 07:11:41 | 2024-10-21 07:11:19 |
      | 24 |     24 | 2024-10-21 07:11:42 | 2024-10-21 07:11:19 |
      | 25 |     25 | 2024-10-21 07:11:43 | 2024-10-21 07:11:19 |
      | 26 |     26 | 2024-10-21 07:11:44 | 2024-10-21 07:11:19 |
      | 27 |     27 | 2024-10-21 07:11:45 | 2024-10-21 07:11:19 |
      | 28 |     28 | 2024-10-21 07:11:46 | 2024-10-21 07:11:19 |
      | 29 |     29 | 2024-10-21 07:11:47 | 2024-10-21 07:11:19 |
      | 30 |     30 | 2024-10-21 07:11:48 | 2024-10-21 07:11:19 |
      +----+--------+---------------------+---------------------+
      

      可以根据在线sql转go结构体来实现,转换之后如下:

      package models
      
      import (
      	"time"
      )
      
      type TestPage struct {
      	Id         int       `gorm:"column:id;type:int(11);AUTO_INCREMENT;primary_key" json:"id"`
      	Number     int       `gorm:"column:number;type:int(11);NOT NULL" json:"number"`
      	CreateTime time.Time `gorm:"column:create_time;type:datetime;default:CURRENT_TIMESTAMP" json:"create_time"`
      	UpdateTime time.Time `gorm:"column:update_time;type:datetime;default:CURRENT_TIMESTAMP" json:"update_time"`
      }
      
      func (m *TestPage) TableName() string {
      	return "test_page"
      }
      

      package types
      
      import "GoTest/gorm_demo/models"
      
      type PageReq struct {
      	Page     int `form:"page,default=1"`
      	PageSize int `form:"page_size,default=4"`
      }
      
      type PageResult struct {
      	TotalPage  int  `json:"total_page"`  //总页数
      	TotalCount int  `json:"total_count"` //总条数
      	Page       int  `json:"page"`        //当前页
      	PageSize   int  `json:"page_size"`   //当前页数据量
      	PrevPage   bool `json:"prev_page"`   //是否存在上页
      	NextPage   bool `json:"next_page"`   //是否存在下页
      }
      
      //要测试的分页结构
      type TestPageResp struct {
      	PageResult
      	List []models.TestPage `json:"list"`
      }
      

      package page
      
      import (
      	"math"
      
      	"GoTest/gorm_demo/types"
      )
      
      // GetOffsetAndLimit
      //
      //	@Description: 获取查数据库的偏移量和限制数
      //	@param page 当前页
      //	@param pageSize 业数据量
      //	@return offset 数据库偏移
      //	@return limit 数据库限制量
      func GetOffsetAndLimit(page, pageSize int) (offset, limit int) {
      	limit = pageSize
      	if limit <= 0 {
      		limit = 4
      	}
      
      	if page > 1 {
      		offset = (page - 1) * limit
      	}
      
      	return
      }
      
      // GetPageResult
      //
      //	@Description: 获取分页结果
      //	@param pg 当前页
      //	@param pageSize 页数据量
      //	@param totalCount 根据此数量进行分页
      //	@return types.PageResult 分页结构
      func GetPageResult(pg, pageSize, totalCount int) types.PageResult {
      	res := types.PageResult{}
      	res.TotalCount = totalCount
      	res.PageSize = pageSize
      	res.Page = pg
      	if pageSize > 0 && totalCount > 0 {
      		res.TotalPage = int(math.Ceil(float64(totalCount) / float64(pageSize)))
      	}
      
      	if res.Page < res.TotalPage {
      		res.NextPage = true
      	}
      	if res.Page > 1 {
      		res.PrevPage = true
      	}
      
      	return res
      }
      

      使用gorm连接mysql

      package conn
      
      import (
      	"fmt"
      	"runtime"
      	"sync"
      	"time"
      
      	"GoTest/comm/logger"
      
      	"go.uber.org/zap"
      	"gorm.io/driver/mysql"
      	"gorm.io/gorm"
      	gormLog "gorm.io/gorm/logger"
      )
      
      type MysqlDb string
      
      const (
      	Xxx MysqlDb = "xxx"
      )
      
      var (
      	mysqlConn = map[MysqlDb]*gorm.DB{}
      	mysqlLock = sync.RWMutex{}
      )
      
      func GetXxxDbConn() *gorm.DB {
      	return getDbPool(Xxx)
      }
      
      func getDbPool(dbKey MysqlDb) *gorm.DB {
      	mysqlLock.RLock()
      	if conn, ok := mysqlConn[dbKey]; ok {
      		mysqlLock.RUnlock()
      
      		return conn
      	}
      	mysqlLock.RUnlock()
      
      	mysqlLock.Lock()
      	defer mysqlLock.Unlock()
      
      	var dsn string
      	switch dbKey {
      	case Xxx:
      		dsn = fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8mb4&parseTime=True&loc=Local&timeout=5s", "xxx", "xxx", "127.0.0.1:3306", "xxx")
      	default:
      		logger.Error("unknown db", zap.String("db", string(dbKey)))
      		return nil
      	}
      
      	conn := newConnect(dsn)
      	mysqlConn[dbKey] = conn
      
      	return conn
      }
      
      func newConnect(dsn string) *gorm.DB {
      	conn, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
      		Logger: gormLog.Default.LogMode(gormLog.Info),
      	})
      	if err != nil {
      		logger.Error("connect mysql error", zap.String("dsn", dsn), zap.Error(err))
      		return nil
      	}
      
      	dbPool, err := conn.DB()
      	if err != nil {
      		logger.Error("get mysql db error", zap.String("dsn", dsn), zap.Error(err))
      		return nil
      	}
      
      	logger.Info("conn mysql success")
      
      	//连接池中中最大空闲时间
      	dbPool.SetConnMaxIdleTime(time.Hour)
      	//连接池中空闲连接的最大数量
      	dbPool.SetMaxIdleConns(runtime.NumCPU() * 5)
      	//数据库连接的最大数量
      	dbPool.SetMaxOpenConns(runtime.NumCPU() * 10)
      	//连接可复用最大时间
      	dbPool.SetConnMaxLifetime(time.Hour * 24)
      
      	return conn
      }
      

      使用时只需要调用GetXxxDbConn函数获取连接即可。

      使用gin框架启动一个http服务,并注册一条路由用于查询列表接口:

      r := gin.New()
      
      	r.GET("/page/list", GetPageListHandler)
      
      	if err := r.Run(":8888"); err != nil {
      		panic(err)
      	}
      

      查询列表接口回调函数实现如下:

      func GetPageListHandler(c *gin.Context) {
      	var req types.PageReq
      
      	if err := c.ShouldBind(&req); err != nil {
      		logger.Error("invalid req", zap.Error(err))
      		c.String(500, "invalid req")
      
      		return
      	}
      
      	logger.Info("get page list req", zap.Any("req", req))
      
      	db := conn.GetXxxDbConn()
      	resp := types.TestPageResp{}
      	resp.List = make([]models.TestPage, 0)
      
      	//获取所有数量
      	var count int64
      	if err := db.Model(&models.TestPage{}).Count(&count).Error; err != nil {
      		logger.Error("get count error", zap.Error(err))
      		c.String(500, "get count error")
      		return
      	}
      
      	//计算分页数据
      	resp.PageResult = page.GetPageResult(req.Page, req.PageSize, int(count))
      
      	//计算偏移量
      	offset, limit := page.GetOffsetAndLimit(req.Page, req.PageSize)
      
      	//数据库查询
      	if err := db.Model(&models.TestPage{}).Order("create_time DESC").Offset(offset).Limit(limit).Find(&resp.List).Error; err != nil {
      		logger.Error("get list error", zap.Error(err))
      		c.String(500, "get list error")
      		return
      	}
      
      	//返回数据
      	c.JSON(http.StatusOK, resp)
      }
      

      控制台输出如下:

      [GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
       - using env:	export GIN_MODE=release
       - using code:	gin.SetMode(gin.ReleaseMode)
      
      [GIN-debug] GET    /page/list                --> main.GetPageListHandler (1 handlers)
      [GIN-debug] [WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.
      Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.
      [GIN-debug] Listening and serving HTTP on :8888
      

      这里使用的是gin框架的debug模式,生产环境中不要用gin框架的debug模式,会影响程序的qps

      使用curl命令来调用,获取第2页,页数据量为6条的数据,控制台输出如下:

      [xxx@xxx ~]# curl -v -X GET --location "http://127.0.0.1:8888/page/list?page=2&page_size=6"
      * About to connect() to 127.0.0.1 port 8888 (#0)
      *   Trying 127.0.0.1...
      * Connected to 127.0.0.1 (127.0.0.1) port 8888 (#0)
      > GET /page/list?page=2&page_size=6 HTTP/1.1
      > User-Agent: curl/7.29.0
      > Host: 127.0.0.1:8888
      > Accept: */*
      > 
      < HTTP/1.1 200 OK
      < Content-Type: application/json; charset=utf-8
      < Date: Mon, 21 Oct 2024 09:23:13 GMT
      < Content-Length: 735
      < 
      * Connection #0 to host 127.0.0.1 left intact
      {
          "total_page": 5,    //总共有5页
          "total_count": 30,  //根据30条数据进行分页
          "page": 2,          //当前页为第2页
          "page_size": 6,     //每一页的大小为6
          "prev_page": true,  //上一页存在
          "next_page": true,  //下一页存在
          "list": [           //根据创建时间倒序的列表
              {
                  "id": 24,
                  "number": 24,
                  "create_time": "2024-10-21T07:11:42+08:00",
                  "update_time": "2024-10-21T07:11:19+08:00"
              },
              {
                  "id": 23,
                  "number": 23,
                  "create_time": "2024-10-21T07:11:41+08:00",
                  "update_time": "2024-10-21T07:11:19+08:00"
              },
              {
                  "id": 22,
                  "number": 22,
                  "create_time": "2024-10-21T07:11:40+08:00",
                  "update_time": "2024-10-21T07:11:19+08:00"
              },
              {
                  "id": 21,
                  "number": 21,
                  "create_time": "2024-10-21T07:11:39+08:00",
                  "update_time": "2024-10-21T07:11:19+08:00"
              },
              {
                  "id": 20,
                  "number": 20,
                  "create_time": "2024-10-21T07:11:38+08:00",
                  "update_time": "2024-10-21T07:11:19+08:00"
              },
              {
                  "id": 19,
                  "number": 19,
                  "create_time": "2024-10-21T07:11:37+08:00",
                  "update_time": "2024-10-21T07:11:19+08:00"
              }
          ]
      }
      

      此时gin程序控制台输出:

      [GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
       - using env:	export GIN_MODE=release
       - using code:	gin.SetMode(gin.ReleaseMode)
      
      [GIN-debug] GET    /page/list                --> main.GetPageListHandler (1 handlers)
      [GIN-debug] [WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.
      Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.
      [GIN-debug] Listening and serving HTTP on :8888
      [2024-10-21 17:23:13.435] | INFO  | Goroutine:34 | [gorm_demo/main.go:131]      | get page list req | {"req": {"Page":2,"PageSize":6}} //分页请求数据
      [2024-10-21 17:23:13.437] | INFO  | Goroutine:34 | [conn/mysql.go:74]           | conn mysql success
      
      2024/10/21 17:23:13 D:/GoTest/gorm_demo/main.go:139
      [0.640ms] [rows:1] SELECT count(*) FROM `test_page`  //查询所有数量的sql
      
      2024/10/21 17:23:13 D:/GoTest/gorm_demo/main.go:152
      [0.325ms] [rows:6] SELECT * FROM `test_page` ORDER BY create_time DESC LIMIT 6 OFFSET 6 //根据分页请求计算出的偏移位置和限制量sql
      

      上面打印了gorm库中的日志信息,可以通过自定义zap对象来实现gormlogger对象来自定义gorm输出日志。

      分页的方法有很多,上面只是给出了其中一种,根据自己项目的业务场景选择一种最喜欢的方法就行。

      以上为个人经验,希望能给大家一个参考,也希望大家多多支持风君子博客。

      您可能感兴趣的文章:

      • Golang开发使用gorm时打印SQL语句方式
      • Golang Gorm实现自定义多态模型关联查询
      • Golang使用gorm实现分页功能的示例代码
      • Golang创建第一个web项目(Gin+Gorm)
      • Golang实现自定义时间结构体并支持Json&Gorm

      站内搜索