Golang如何用gorm實現(xiàn)分頁的功能
背景
在提供列表接口時一般要用到分頁,對于存儲在某些數(shù)據(jù)庫中的數(shù)據(jù)進行分頁起來非常的方便,下文給出一個通過gorm進行分頁并通過http返回數(shù)據(jù)的例子。
go庫下載
gorm官方文檔地址:https://gorm.io/zh_CN/docs/,庫下載如下:
go get -u gorm.io/gorm
本文使用的數(shù)據(jù)庫是mysql,因此要下載mysql驅(qū)動:
go get -u gorm.io/driver/mysql
這里使用gin框架來提供查詢列表的接口想,庫下載如下:
go get -u github.com/gin-gonic/gin
初始化數(shù)據(jù)
【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】插入數(shù)據(jù)
插入30條number和create_time字段不同的數(shù)據(jù),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);
【3】查看數(shù)據(jù)
查看創(chuàng)建出來的30條數(shù)據(jù):
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 | +----+--------+---------------------+---------------------+
代碼示例
【1】gorm結(jié)構(gòu)體定義
可以根據(jù)在線sql轉(zhuǎn)go結(jié)構(gòu)體來實現(xiàn),轉(zhuǎn)換之后如下:
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" }
【2】分頁結(jié)構(gòu)體定義
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"` //總頁數(shù) TotalCount int `json:"total_count"` //總條數(shù) Page int `json:"page"` //當(dāng)前頁 PageSize int `json:"page_size"` //當(dāng)前頁數(shù)據(jù)量 PrevPage bool `json:"prev_page"` //是否存在上頁 NextPage bool `json:"next_page"` //是否存在下頁 } //要測試的分頁結(jié)構(gòu) type TestPageResp struct { PageResult List []models.TestPage `json:"list"` }
【3】封裝分頁方法
package page import ( "math" "GoTest/gorm_demo/types" ) // GetOffsetAndLimit // // @Description: 獲取查數(shù)據(jù)庫的偏移量和限制數(shù) // @param page 當(dāng)前頁 // @param pageSize 業(yè)數(shù)據(jù)量 // @return offset 數(shù)據(jù)庫偏移 // @return limit 數(shù)據(jù)庫限制量 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: 獲取分頁結(jié)果 // @param pg 當(dāng)前頁 // @param pageSize 頁數(shù)據(jù)量 // @param totalCount 根據(jù)此數(shù)量進行分頁 // @return types.PageResult 分頁結(jié)構(gòu) 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】封裝獲取數(shù)據(jù)庫連接方法
使用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) //連接池中空閑連接的最大數(shù)量 dbPool.SetMaxIdleConns(runtime.NumCPU() * 5) //數(shù)據(jù)庫連接的最大數(shù)量 dbPool.SetMaxOpenConns(runtime.NumCPU() * 10) //連接可復(fù)用最大時間 dbPool.SetConnMaxLifetime(time.Hour * 24) return conn }
使用時只需要調(diào)用GetXxxDbConn函數(shù)獲取連接即可。
【5】查詢列表接口
使用gin框架啟動一個http服務(wù),并注冊一條路由用于查詢列表接口:
r := gin.New() r.GET("/page/list", GetPageListHandler) if err := r.Run(":8888"); err != nil { panic(err) }
查詢列表接口回調(diào)函數(shù)實現(xiàn)如下:
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) //獲取所有數(shù)量 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 } //計算分頁數(shù)據(jù) resp.PageResult = page.GetPageResult(req.Page, req.PageSize, int(count)) //計算偏移量 offset, limit := page.GetOffsetAndLimit(req.Page, req.PageSize) //數(shù)據(jù)庫查詢 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 } //返回數(shù)據(jù) c.JSON(http.StatusOK, resp) }
【6】啟動http服務(wù)
控制臺輸出如下:
[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模式,生產(chǎn)環(huán)境中不要用gin框架的debug模式,會影響程序的qps。
【7】調(diào)用獲取列表接口
使用curl命令來調(diào)用,獲取第2頁,頁數(shù)據(jù)量為6條的數(shù)據(jù),控制臺輸出如下:
[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, //根據(jù)30條數(shù)據(jù)進行分頁 "page": 2, //當(dāng)前頁為第2頁 "page_size": 6, //每一頁的大小為6 "prev_page": true, //上一頁存在 "next_page": true, //下一頁存在 "list": [ //根據(jù)創(chuàng)建時間倒序的列表 { "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}} //分頁請求數(shù)據(jù) [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` //查詢所有數(shù)量的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 //根據(jù)分頁請求計算出的偏移位置和限制量sql
上面打印了gorm庫中的日志信息,可以通過自定義zap對象來實現(xiàn)gorm的logger對象來自定義gorm輸出日志。
總結(jié)
分頁的方法有很多,上面只是給出了其中一種,根據(jù)自己項目的業(yè)務(wù)場景選擇一種最喜歡的方法就行。
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
golang實現(xiàn)簡易的分布式系統(tǒng)方法
這篇文章主要介紹了golang實現(xiàn)簡易的分布式系統(tǒng)方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-10-10golang協(xié)程關(guān)閉踩坑實戰(zhàn)記錄
協(xié)程(coroutine)是Go語言中的輕量級線程實現(xiàn),下面這篇文章主要給大家介紹了關(guān)于golang協(xié)程關(guān)閉踩坑的相關(guān)資料,文中通過實例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-03-03go從指定的URL下載圖片并保存到本地的代碼實現(xiàn)
這段代碼定義了一個名為 downloadImage 的函數(shù),其目的是從指定的URL下載圖片并保存到本地文件系統(tǒng),本文是對代碼功能的詳細(xì)描述,對大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下2024-08-08Go?不支持?[]T轉(zhuǎn)換為[]interface類型詳解
這篇文章主要為大家介紹了Go不支持[]T轉(zhuǎn)換為[]interface類型詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-01-01