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

2025-06-25 17:50
文章标签 实现 golang 功能 分页 gorm

本文主要是介绍Golang如何用gorm实现分页的功能,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

《Golang如何用gorm实现分页的功能》:本文主要介绍Golang如何用gorm实现分页的功能方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教...

背景

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

go库下载

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

初始化数据

【1】建表

建一个用来测试分页的表,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 '测试分页表';

【2】插入数据

插入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, www.chinasem.cnNOW() + 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);

【3】查看数据

查看创建出来的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 |
| 12js |     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 |
+----+--------+---------------------+---------------------+

4、代码示例

【1】gorm结构体定义

可以根据在线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:CURjsRENT_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"
}

【2】分页结构体定义

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"`
}

【3】封装分页方法

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
}

【4】封装获取数据库连接方法

使用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函数获取连接即可。

【5】查询列表接口

使用gChina编程in框架启动一个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)
}

【6】启动http服务

控制台输出如下:

[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

【7】调用获取列表接口

使用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"
javascript        },
        {
            "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输出日志。

总结

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

以上为个人经验,希望能给大家一个参考,也希望大家多多支持China编程(www.chinasem.cn)。

这篇关于Golang如何用gorm实现分页的功能的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1155211

相关文章

C#借助Spire.XLS for .NET实现在Excel中添加文档属性

《C#借助Spire.XLSfor.NET实现在Excel中添加文档属性》在日常的数据处理和项目管理中,Excel文档扮演着举足轻重的角色,本文将深入探讨如何在C#中借助强大的第三方库Spire.... 目录为什么需要程序化添加Excel文档属性使用Spire.XLS for .NET库实现文档属性管理Sp

Python+FFmpeg实现视频自动化处理的完整指南

《Python+FFmpeg实现视频自动化处理的完整指南》本文总结了一套在Python中使用subprocess.run调用FFmpeg进行视频自动化处理的解决方案,涵盖了跨平台硬件加速、中间素材处理... 目录一、 跨平台硬件加速:统一接口设计1. 核心映射逻辑2. python 实现代码二、 中间素材处

Java数组动态扩容的实现示例

《Java数组动态扩容的实现示例》本文主要介绍了Java数组动态扩容的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录1 问题2 方法3 结语1 问题实现动态的给数组添加元素效果,实现对数组扩容,原始数组使用静态分配

Python实现快速扫描目标主机的开放端口和服务

《Python实现快速扫描目标主机的开放端口和服务》这篇文章主要为大家详细介绍了如何使用Python编写一个功能强大的端口扫描器脚本,实现快速扫描目标主机的开放端口和服务,感兴趣的小伙伴可以了解下... 目录功能介绍场景应用1. 网络安全审计2. 系统管理维护3. 网络故障排查4. 合规性检查报错处理1.

Python轻松实现Word到Markdown的转换

《Python轻松实现Word到Markdown的转换》在文档管理、内容发布等场景中,将Word转换为Markdown格式是常见需求,本文将介绍如何使用FreeSpire.DocforPython实现... 目录一、工具简介二、核心转换实现1. 基础单文件转换2. 批量转换Word文件三、工具特性分析优点局

Springboot3统一返回类设计全过程(从问题到实现)

《Springboot3统一返回类设计全过程(从问题到实现)》文章介绍了如何在SpringBoot3中设计一个统一返回类,以实现前后端接口返回格式的一致性,该类包含状态码、描述信息、业务数据和时间戳,... 目录Spring Boot 3 统一返回类设计:从问题到实现一、核心需求:统一返回类要解决什么问题?

Java使用Spire.Doc for Java实现Word自动化插入图片

《Java使用Spire.DocforJava实现Word自动化插入图片》在日常工作中,Word文档是不可或缺的工具,而图片作为信息传达的重要载体,其在文档中的插入与布局显得尤为关键,下面我们就来... 目录1. Spire.Doc for Java库介绍与安装2. 使用特定的环绕方式插入图片3. 在指定位

Java使用Spire.Barcode for Java实现条形码生成与识别

《Java使用Spire.BarcodeforJava实现条形码生成与识别》在现代商业和技术领域,条形码无处不在,本教程将引导您深入了解如何在您的Java项目中利用Spire.Barcodefor... 目录1. Spire.Barcode for Java 简介与环境配置2. 使用 Spire.Barco

Java利用Spire.Doc for Java实现在模板的基础上创建Word文档

《Java利用Spire.DocforJava实现在模板的基础上创建Word文档》在日常开发中,我们经常需要根据特定数据动态生成Word文档,本文将深入探讨如何利用强大的Java库Spire.Do... 目录1. Spire.Doc for Java 库介绍与安装特点与优势Maven 依赖配置2. 通过替换

Android使用java实现网络连通性检查详解

《Android使用java实现网络连通性检查详解》这篇文章主要为大家详细介绍了Android使用java实现网络连通性检查的相关知识,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录NetCheck.Java(可直接拷贝)使用示例(Activity/Fragment 内)权限要求