gormt中分页查询的使用

本文介绍了如何在Gormt项目中添加分页查询支持,包括IPage接口、Page类的详细说明,以及SelectPage函数的使用。自定义IPage接口的示例展示了如何满足特定需求,例如限制分页按钮的数量。此外,还提供了测试用例来演示分页查询的使用。
摘要由CSDN通过智能技术生成

原创不易,未经允许,请勿转载。

gormt的Github链接

最近因为项目需要,所以使用了gormt这个项目,用来自动生成struct等代码。但是美中不足的是它里面生成的一些函数不支持分页查询,所以我就参考之前Java使用的Mybatis-Plus中的分页插件,在gormt中实现了对分页查询的支持。当需要进行分页查询的操作时,我们只需要传入每个分页的大小、当前的页数(从1开始计算),以及排序条件、筛选条件即可,不需要再去关心偏移量等一些复杂的操作。
接下来我简单介绍一下增加的内容以及如何使用

如何使用?

分页查询的功能默认是开启的,IPagePage的内容在生成的gen.page.go文件中,SelectPage函数则是在dbname.gen.tablename.go文件中
可以通过配置文件中的这个选项选择是否开启

is_out_page: true # 是否输出分页函数 

IPage接口的介绍

这里提供了一个IPage接口,定义了以下方法,作用如注释所示

type IPage interface {
	GetRecords() interface{}      // 获取查询的记录
	SetRecords(interface{})       // 设置查询的记录
	GetTotal() int64              // 获取总记录数
	SetTotal(int64)               // 设置总记录数
	GetCurrent() int64            // 获取当前页
	SetCurrent(int64)             // 设置当前页
	GetSize() int64               // 获取每页显示大小
	SetSize(int64)                // 设置每页显示大小
	AddOrderItem(OrderItem)       // 设置排序条件
	AddOrderItems([]OrderItem)    // 批量设置排序条件
	GetOrederItemsString() string // 将排序条件拼接成字符串
	Offset() int64                // 获取偏移量
	GetPages() int64              // 获取总的分页数
}

IPage的基础上,提供了一个默认实现Page,它实现了IPage中的所有方法。

type Page struct {
	total   int64       // 总的记录数
	size    int64       // 每页显示的大小
	current int64       // 当前页
	orders  []OrderItem // 排序条件
	Records interface{} // 查询数据列表

}

func (page *Page) GetRecords() interface{} {
	return page.Records
}

func (page *Page) SetRecords(records interface{}) {
	page.Records = records
}

func (page *Page) GetTotal() int64 {
	return page.total
}

func (page *Page) SetTotal(total int64) {
	page.total = total

}

func (page *Page) GetCurrent() int64 {
	return page.current
}

func (page *Page) SetCurrent(current int64) {
	page.current = current
}

func (page *Page) GetSize() int64 {
	return page.size
}
func (page *Page) SetSize(size int64) {
	page.size = size

}

func (page *Page) AddOrderItem(orderItem OrderItem) {
	page.orders = append(page.orders, orderItem)
}

func (page *Page) AddOrderItems(orderItems []OrderItem) {
	page.orders = append(page.orders, orderItems...)
}

func (page *Page) GetOrederItemsString() string {
	arr := make([]string, 0)
	var order string

	for _, val := range page.orders {
		if val.asc {
			order = ""
		} else {
			order = "desc"
		}
		arr = append(arr, fmt.Sprintf("%s %s", val.column, order))
	}
	return strings.Join(arr, ",")
}

func (page *Page) Offset() int64 {
	if page.GetCurrent() > 0 {
		return (page.GetCurrent() - 1) * page.GetSize()
	} else {
		return 0
	}
}

func (page *Page) GetPages() int64 {
	if page.GetSize() == 0 {
		return 0
	}
	pages := page.GetTotal() / page.GetSize()
	if page.GetTotal()%page.size != 0 {
		pages++
	}

	return pages
}


// 提供了一个构造方法,需要传入每个分页的大小,以及当前所处的页
func NewPage(size, current int64, orderItems ...OrderItem) *Page {
	return &Page{size: size, current: current, orders: orderItems}
}

IPageOrderItem是用作于排序的,定义如下,并且提供了两种构造方法

type OrderItem struct {
	column string // 需要排序的字段
	asc    bool   // 是否正序排列,默认true
}

func BuildAsc(column string) OrderItem {
	return OrderItem{column: column, asc: true}
}

func BuildDesc(column string) OrderItem {
	return OrderItem{column: column, asc: false}
}

func BuildAscs(columns ...string) []OrderItem {
	items := make([]OrderItem, 0)
	for _, val := range columns {
		items = append(items, BuildAsc(val))
	}
	return items
}

func BuildDescs(columns ...string) []OrderItem {
	items := make([]OrderItem, 0)
	for _, val := range columns {
		items = append(items, BuildDesc(val))
	}
	return items
}

SelectPage函数的介绍

假设查询的表结构如下所示

type BlogAdmin struct {
	ID        uint       `gorm:"primaryKey;column:id;type:int unsigned;not null" json:"-"`                            // 用户ID
	CreatedAt time.Time  `gorm:"column:created_at;type:datetime;not null;default:CURRENT_TIMESTAMP" json:"createdAt"` // 创建时间
	UpdatedAt time.Time  `gorm:"column:updated_at;type:datetime;not null;default:CURRENT_TIMESTAMP" json:"updatedAt"` // 修改时间
	DeletedAt *time.Time `gorm:"index:idx_deleted;column:deleted_at;type:datetime" json:"deletedAt"`                  // 删除时间
	Username  string     `gorm:"unique;unique;column:username;type:varchar(255);not null" json:"username"`            // 用户名
	Password  string     `gorm:"column:password;type:varchar(255);not null" json:"password"`                          // 密码
}

下面是生成的对BlogAdmin这个表的分页查询函数。一共需要传入两个参数

  • page:实现了IPage接口的类都可以,默认可以使用Page
  • opts: 查询条件

返回值:

  • resultPage:实现了IPage接口,默认为Page。查询的记录将保存将通过SetRecords保存。
  • err:错误

首先先获取出查询条件,根据查询条件,先查询出满足条件的记录,然后统计记录数,接着看是否需要排序,如果需要排序的话,则先进行排序操作。最后获取页面大小和偏移量,把查询结果保存到results里面。
因为BlogAdmin的表并未设置外键,所以下列代码中并未生成预加载部分的代码。

// SelectPage 分页查询
func (obj *_BlogAdminMgr) SelectPage(page IPage, opts ...Option) (resultPage IPage, err error) {
	options := options{
		query: make(map[string]interface{}, len(opts)),
	}
	for _, o := range opts {
		o.apply(&options)
	}
	resultPage = page
	results := make([]BlogAdmin, 0)
	var count int64 // 统计总的记录数
	query := obj.DB.WithContext(obj.ctx).Model(BlogAdmin{}).Where(options.query)
	query.Count(&count)
	resultPage.SetTotal(count)
	if len(page.GetOrederItemsString()) > 0 {
		query = query.Order(page.GetOrederItemsString())
	}
	err = query.Limit(int(page.GetSize())).Offset(int(page.Offset())).Find(&results).Error

	resultPage.SetRecords(results)
	return
}

自定义IPage

如果说Page不能满足您的使用需要的话,可以自定义一个IPage的实现类。最简单的做法是继承Page,然后定制您所需要的方法即可。

type MyPage struct {
    Page
    ...//一些拓展的字段
}

// 可以是新增的方法,也可以是重写Page实现好的方法,主要要使用指针类型
func (page *MyPage) someFunc(...) {
 	...   
}


假设现在有这么个需求,在前端分页展示的时候,我们不希望展示出所有的按钮,假设页面数量很多,但我们只需要固定展示10个分页按钮的话,效果如下。那单纯使用Page就很难满足这个需求,还需要额外的一些处理。所以可以自定义IPage的实现类,代码如下。

在这里插入图片描述


type MyPage struct {
    Page
	begin int64  // 第一个分页按钮的页码
    end   int64  // 最后一个分页按钮的页码
    showPages int // 展示的页面数
}

func (page *MyPage) setShowBtnNum() {
    if(page.GetPages() < page.showPages) {
    	page.begin = 1
        if page.GetPages() > 1 {
            page.end = page.GetPages()
        } else {
            page.end = 1  
        }
    } else {
        page.begin = page.GetCurrent() - (page.showPages/2)
        if page.showPages %2 == 0 {
   			page.end = page.GetCurrent() + (page.showPages/2 - 1)
        } else {
            page.end = page.GetCurrent() + page.showPages/2
        }
     	
        if page.begin < 1 {
        
        	page.begin = 1
            page.end = page.begin + page.showPages - 1;
        }
        if page.end > page.showPages {
            page.end = page.GetCurrent()
            page.begin = page.end - (page.showPages - 1)
        }
    }
}

简单的使用案例

下面使用的结构体由下表生成的
https://github.com/xxjwxc/gormt/blob/master/data/view/genfunc/model/matrix.sql


func GetGorm(dataSourceName string) *gorm.DB {
	db, err := gorm.Open(mysql.Open(dataSourceName), &gorm.Config{PrepareStmt: false})
	if err != nil {
		panic(err)
	}
	sqlDB, err := db.DB()
	if err != nil {
		panic(err)
	}
	// SetMaxIdleConns 设置空闲连接池中连接的最大数量
	sqlDB.SetMaxIdleConns(10)

	// SetMaxOpenConns 设置打开数据库连接的最大数量。
	sqlDB.SetMaxOpenConns(100)

	// SetConnMaxLifetime 设置了连接可复用的最大时间。
	sqlDB.SetConnMaxLifetime(time.Hour)

	return db.Debug()
}

// TestSelectPage 测试分页查询功能
func TestSelectPage(t *testing.T) {
	model.OpenRelated() // 打开全局预加载 (外键)

	db := GetGorm("root:123456@tcp(192.168.253.100:3306)/gormt?charset=utf8&parseTime=True&loc=Local&interpolateParams=True")
	defer func() {
		sqldb, _ := db.DB()
		sqldb.Close()
	}()

	accountMgr := model.AccountMgr(db)
	// 构造分页条件
	page := model.NewPage(1, 1)
	// 不带条件的查询
	result, err := accountMgr.SelectPage(page)
	fmt.Println("不带条件查询")
	fmt.Println(err)
	fmt.Println(result.GetPages())
	fmt.Println(result.GetTotal())
	for _, val := range result.GetRecords().([]model.Account) {
		fmt.Println(val)
	}

	//带条件查询
	page = model.NewPage(1, 2)
	result, err = accountMgr.SelectPage(page, accountMgr.WithName("bbbb"))
	fmt.Println("带条件查询")
	fmt.Println(err)
	fmt.Println(result.GetPages())
	fmt.Println(result.GetTotal())
	for _, val := range result.GetRecords().([]model.Account) {
		fmt.Println(val)
	}

	// 进行排序,按name字段降序
	page = model.NewPage(1, 1, model.BuildDesc("name"))
	result, err = accountMgr.SelectPage(page)
	fmt.Println("进行排序,按name字段降序")
	fmt.Println(err)
	fmt.Println(result.GetPages())
	fmt.Println(result.GetTotal())
	for _, val := range result.GetRecords().([]model.Account) {
		fmt.Println(val)
	}
}

gormt的Github链接
最近因为项目需要,所以使用了gormt这个项目,用来自动生成struct等代码。但是美中不足的是它里面生成的一些函数不支持分页查询,所以我就参考之前Java使用的Mybatis-Plus中的分页插件,在gormt中实现了对分页查询的支持。当需要进行分页查询的操作时,我们只需要传入每个分页的大小、当前的页数(从1开始计算),以及排序条件、筛选条件即可,不需要再去关心偏移量等一些复杂的操作。
接下来我简单介绍一下增加的内容以及如何使用

IPage和Page的内容在生成的gen.page.go文件中,SelectPage函数则是在dbname.gen.tablename.go文件中

IPage接口的介绍

这里提供了一个IPage接口,定义了以下方法,作用如注释所示

type IPage interface {
	GetRecords() interface{}      // 获取查询的记录
	SetRecords(interface{})       // 设置查询的记录
	GetTotal() int64              // 获取总记录数
	SetTotal(int64)               // 设置总记录数
	GetCurrent() int64            // 获取当前页
	SetCurrent(int64)             // 设置当前页
	GetSize() int64               // 获取每页显示大小
	SetSize(int64)                // 设置每页显示大小
	AddOrderItem(OrderItem)       // 设置排序条件
	AddOrderItems([]OrderItem)    // 批量设置排序条件
	GetOrederItemsString() string // 将排序条件拼接成字符串
	Offset() int64                // 获取偏移量
	GetPages() int64              // 获取总的分页数
}

IPage的基础上,提供了一个默认实现Page,它实现了IPage中的所有方法。

type Page struct {
	total   int64       // 总的记录数
	size    int64       // 每页显示的大小
	current int64       // 当前页
	orders  []OrderItem // 排序条件
	Records interface{} // 查询数据列表

}

func (page *Page) GetRecords() interface{} {
	return page.Records
}

func (page *Page) SetRecords(records interface{}) {
	page.Records = records
}

func (page *Page) GetTotal() int64 {
	return page.total
}

func (page *Page) SetTotal(total int64) {
	page.total = total

}

func (page *Page) GetCurrent() int64 {
	return page.current
}

func (page *Page) SetCurrent(current int64) {
	page.current = current
}

func (page *Page) GetSize() int64 {
	return page.size
}
func (page *Page) SetSize(size int64) {
	page.size = size

}

func (page *Page) AddOrderItem(orderItem OrderItem) {
	page.orders = append(page.orders, orderItem)
}

func (page *Page) AddOrderItems(orderItems []OrderItem) {
	page.orders = append(page.orders, orderItems...)
}

func (page *Page) GetOrederItemsString() string {
	arr := make([]string, 0)
	var order string

	for _, val := range page.orders {
		if val.asc {
			order = ""
		} else {
			order = "desc"
		}
		arr = append(arr, fmt.Sprintf("%s %s", val.column, order))
	}
	return strings.Join(arr, ",")
}

func (page *Page) Offset() int64 {
	if page.GetCurrent() > 0 {
		return (page.GetCurrent() - 1) * page.GetSize()
	} else {
		return 0
	}
}

func (page *Page) GetPages() int64 {
	if page.GetSize() == 0 {
		return 0
	}
	pages := page.GetTotal() / page.GetSize()
	if page.GetTotal()%page.size != 0 {
		pages++
	}

	return pages
}


// 提供了一个构造方法,需要传入每个分页的大小,以及当前所处的页
func NewPage(size, current int64, orderItems ...OrderItem) *Page {
	return &Page{size: size, current: current, orders: orderItems}
}

IPageOrderItem是用作于排序的,定义如下,并且提供了两种构造方法

type OrderItem struct {
	column string // 需要排序的字段
	asc    bool   // 是否正序排列,默认true
}

func BuildAsc(column string) OrderItem {
	return OrderItem{column: column, asc: true}
}

func BuildDesc(column string) OrderItem {
	return OrderItem{column: column, asc: false}
}

func BuildAscs(columns ...string) []OrderItem {
	items := make([]OrderItem, 0)
	for _, val := range columns {
		items = append(items, BuildAsc(val))
	}
	return items
}

func BuildDescs(columns ...string) []OrderItem {
	items := make([]OrderItem, 0)
	for _, val := range columns {
		items = append(items, BuildDesc(val))
	}
	return items
}

SelectPage函数的介绍

假设查询的表结构如下所示

type BlogAdmin struct {
	ID        uint       `gorm:"primaryKey;column:id;type:int unsigned;not null" json:"-"`                            // 用户ID
	CreatedAt time.Time  `gorm:"column:created_at;type:datetime;not null;default:CURRENT_TIMESTAMP" json:"createdAt"` // 创建时间
	UpdatedAt time.Time  `gorm:"column:updated_at;type:datetime;not null;default:CURRENT_TIMESTAMP" json:"updatedAt"` // 修改时间
	DeletedAt *time.Time `gorm:"index:idx_deleted;column:deleted_at;type:datetime" json:"deletedAt"`                  // 删除时间
	Username  string     `gorm:"unique;unique;column:username;type:varchar(255);not null" json:"username"`            // 用户名
	Password  string     `gorm:"column:password;type:varchar(255);not null" json:"password"`                          // 密码
}

下面是生成的对BlogAdmin这个表的分页查询函数。一共需要传入两个参数

  • page:实现了IPage接口的类都可以,默认可以使用Page
  • opts: 查询条件

返回值:

  • resultPage:实现了IPage接口,默认为Page。查询的记录将保存将通过SetRecords保存。
  • err:错误

首先先获取出查询条件,根据查询条件,先查询出满足条件的记录,然后统计记录数,接着看是否需要排序,如果需要排序的话,则先进行排序操作。最后获取页面大小和偏移量,把查询结果保存到results里面。
因为BlogAdmin的表并未设置外键,所以下列代码中并未生成预加载部分的代码。

// SelectPage 分页查询
func (obj *_BlogAdminMgr) SelectPage(page IPage, opts ...Option) (resultPage IPage, err error) {
	options := options{
		query: make(map[string]interface{}, len(opts)),
	}
	for _, o := range opts {
		o.apply(&options)
	}
	resultPage = page
	results := make([]BlogAdmin, 0)
	var count int64 // 统计总的记录数
	query := obj.DB.WithContext(obj.ctx).Model(BlogAdmin{}).Where(options.query)
	query.Count(&count)
	resultPage.SetTotal(count)
	if len(page.GetOrederItemsString()) > 0 {
		query = query.Order(page.GetOrederItemsString())
	}
	err = query.Limit(int(page.GetSize())).Offset(int(page.Offset())).Find(&results).Error

	resultPage.SetRecords(results)
	return
}

自定义IPage

如果说Page不能满足您的使用需要的话,可以自定义一个IPage的实现类。最简单的做法是继承Page,然后定制您所需要的方法即可。

type MyPage struct {
    Page
    ...//一些拓展的字段
}

// 可以是新增的方法,也可以是重写Page实现好的方法,主要要使用指针类型
func (page *MyPage) someFunc(...) {
 	...   
}


假设现在有这么个需求,在前端分页展示的时候,我们不希望展示出所有的按钮,假设页面数量很多,但我们只需要固定展示10个分页按钮的话,效果如下。那单纯使用Page就很难满足这个需求,还需要额外的一些处理。所以可以自定义IPage的实现类,代码如下。

在这里插入图片描述


type MyPage struct {
    Page
	begin int64  // 第一个分页按钮的页码
    end   int64  // 最后一个分页按钮的页码
    showPages int // 展示的页面数
}

func (page *MyPage) setShowBtnNum() {
    if(page.GetPages() < page.showPages) {
    	page.begin = 1
        if page.GetPages() > 1 {
            page.end = page.GetPages()
        } else {
            page.end = 1  
        }
    } else {
        page.begin = page.GetCurrent() - (page.showPages/2)
        if page.showPages %2 == 0 {
   			page.end = page.GetCurrent() + (page.showPages/2 - 1)
        } else {
            page.end = page.GetCurrent() + page.showPages/2
        }
     	
        if page.begin < 1 {
        
        	page.begin = 1
            page.end = page.begin + page.showPages - 1;
        }
        if page.end > page.showPages {
            page.end = page.GetCurrent()
            page.begin = page.end - (page.showPages - 1)
        }
    }
}

简单的使用案例

下面使用的结构体由下表生成的
https://github.com/xxjwxc/gormt/blob/master/data/view/genfunc/model/matrix.sql


func GetGorm(dataSourceName string) *gorm.DB {
	db, err := gorm.Open(mysql.Open(dataSourceName), &gorm.Config{PrepareStmt: false})
	if err != nil {
		panic(err)
	}
	sqlDB, err := db.DB()
	if err != nil {
		panic(err)
	}
	// SetMaxIdleConns 设置空闲连接池中连接的最大数量
	sqlDB.SetMaxIdleConns(10)

	// SetMaxOpenConns 设置打开数据库连接的最大数量。
	sqlDB.SetMaxOpenConns(100)

	// SetConnMaxLifetime 设置了连接可复用的最大时间。
	sqlDB.SetConnMaxLifetime(time.Hour)

	return db.Debug()
}

// TestSelectPage 测试分页查询功能
func TestSelectPage(t *testing.T) {
	model.OpenRelated() // 打开全局预加载 (外键)

	db := GetGorm("root:123456@tcp(192.168.253.100:3306)/gormt?charset=utf8&parseTime=True&loc=Local&interpolateParams=True")
	defer func() {
		sqldb, _ := db.DB()
		sqldb.Close()
	}()

	accountMgr := model.AccountMgr(db)
	// 构造分页条件
	page := model.NewPage(1, 1)
	// 不带条件的查询
	result, err := accountMgr.SelectPage(page)
	fmt.Println("不带条件查询")
	fmt.Println(err)
	fmt.Println(result.GetPages())
	fmt.Println(result.GetTotal())
	for _, val := range result.GetRecords().([]model.Account) {
		fmt.Println(val)
	}

	//带条件查询
	page = model.NewPage(1, 2)
	result, err = accountMgr.SelectPage(page, accountMgr.WithName("bbbb"))
	fmt.Println("带条件查询")
	fmt.Println(err)
	fmt.Println(result.GetPages())
	fmt.Println(result.GetTotal())
	for _, val := range result.GetRecords().([]model.Account) {
		fmt.Println(val)
	}

	// 进行排序,按name字段降序
	page = model.NewPage(1, 1, model.BuildDesc("name"))
	result, err = accountMgr.SelectPage(page)
	fmt.Println("进行排序,按name字段降序")
	fmt.Println(err)
	fmt.Println(result.GetPages())
	fmt.Println(result.GetTotal())
	for _, val := range result.GetRecords().([]model.Account) {
		fmt.Println(val)
	}
}

拒绝白嫖从一键三连开始!

原创不易,未经允许,请勿转载。

博客主页:https://xiaojujiang.blog.csdn.net/

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

jiangxiaoju

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值