使用gorm.Scopes函数实现复用查询逻辑示例

 更新时间:2023年12月19日 12:00:18   作者:Go学堂  
这篇文章主要为大家介绍了使用gorm.Scopes函数实现复用查询逻辑示例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

引言

今天要学习的是gorm.Scopes函数的使用。该函数的作用就是复用查询条件。

gorm Scopes是什么

在项目中,你一定会遇到过很多需要复用的查询条件。比如常用的场景有分页、查询时判定数据权限等操作。

比如,我们有两个数据资源:用户列表和部门列表。那么,在查询列表的时候都会涉及到分页。当然可以在每个列表中都增加上列表相关的查询。同时,也可以将分页的查询抽取出来,做成公共的函数。

那怎么将抽取出来的分页条件在每个列表中都能复用呢?那就是使用gorm.Scopes函数。

我们先看一个使用gorm.Scopes函数使用的简单例子,这个例子只是为了说明gorm.Scopes函数的使用。如下:

func AmountGreaterThan1000(db *gorm.DB) *gorm.DB {
  return db.Where("amount > ?", 1000)
}
func PaidWithCreditCard(db *gorm.DB) *gorm.DB {
  return db.Where("pay_mode_sign = ?", "C")
}
func PaidWithCod(db *gorm.DB) *gorm.DB {
  return db.Where("pay_mode_sign = ?", "C")
}
func OrderStatus(status []string) func (db *gorm.DB) *gorm.DB {
  return func (db *gorm.DB) *gorm.DB {
    return db.Where("status IN (?)", status)
  }
}
// 查找所有金额大于 1000 的信用卡订单
db.Scopes(AmountGreaterThan1000, PaidWithCreditCard).Find(&orders)
// 查找所有金额大于 1000 的 COD 订单
db.Scopes(AmountGreaterThan1000, PaidWithCod).Find(&orders)
// 查找所有金额大于1000 的已付款或已发货订单
db.Scopes(AmountGreaterThan1000, OrderStatus([]string{"paid", "shipped"})).Find(&orders)

通过上述例子可以知道,前4个函数都是func (db *gorm.DB) *gorm.DB 类型的函数,即输入一个db,然后返回一个db。在该函数中的业务逻辑其实就是最常见的db.Wheredb.Offset等常用的查询条件语句而已。只不过是对这种公共的查询语句进行了提取并进行复用而已。

然后将这样的函数传递给ScopesScopes函数只是简单的将func (db *gorm.DB) *gorm.DB放到Statement.scopes这个切片中。

最后,在最终执行的时候,会循环遍历Statement.scopes切片,依次执行该切片中的每一个func (db *gorm.DB) *gorm.DB函数。这样,就把提取出来的公共的查询条件融合在一起了。

使用场景1 -- 分页

当然,我们在查询时最常用的就是分页功能。那么,如何使用gorm.Scopes实现分页查询的复用呢。如下:

func Paginate(r *http.Request) func(db *gorm.DB) *gorm.DB {
  return func (db *gorm.DB) *gorm.DB {
    q := r.URL.Query()
    page, _ := strconv.Atoi(q.Get("page"))
    if page <= 0 {
      page = 1
    }
    pageSize, _ := strconv.Atoi(q.Get("page_size"))
    switch {
    case pageSize > 100:
      pageSize = 100
    case pageSize <= 0:
      pageSize = 10
    }
    offset := (page - 1) * pageSize
    return db.Offset(offset).Limit(pageSize)
  }
}
db.Scopes(Paginate(r)).Find(&users)
db.Scopes(Paginate(r)).Find(&articles)

你看,先定义了一个分页的函数Paginate函数,该函数接收一个*http.Request参数,然后返回一个func(db *gorm.DB) *gorm.DB的函数。因为gorm.Scopes函数只接受func(db *gorm.DB) *gorm.DB类型的函数。最后,将Paginate函数传递给Scopes函数即可。

使用场景2 -- 数据权限

在go-admin开源项目中,我们还发现了一个典型的应用,就是数据权限。在我们的系统中,会遇到这样的场景:一些数据只能自己查看或操作;或者你的上级也能查看或操作;或者同部门的人员能查看或操作自己部门的数据,但不能查看或操作其他部门的权限;又或者只能查看同部门的数据但不能操作同部门的数据等等。

在go-admin中,就使用了gorm.Scopes函数来统一使用权限查询条件。在每个操作中,都通过Scopes函数传入了一个Permission函数。Permissioin函数是根据不同角色拥有的权限,转换成对应的sql语句。如下:

// DeleteAction 通用删除动作
func DeleteAction(control dto.Control) gin.HandlerFunc {
 return func(c *gin.Context) {
  db, err := pkg.GetOrm(c)
        ...//省略了一些逻辑
  //数据权限检查
  p := GetPermissionFromContext(c)
        // 将Permission函数传入Scopes函数
  db = db.WithContext(c).Scopes(
   Permission(object.TableName(), p),
  ).Where(req.GetId()).Delete(object)
        ...//省略了一些逻辑
        // 检查操作的行数,如果操作的行数是0,说明没有权限
  if db.RowsAffected == 0 {
   response.Error(c, http.StatusForbidden, nil, "无权删除该数据")
   return
  }
  response.OK(c, object.GetId(), "删除成功")
  c.Next()
 }
}
// Permission函数的逻辑
// 根据不同 的数据范围枚举值,转换成不同的Where条件
func Permission(tableName string, p *DataPermission) func(db *gorm.DB) *gorm.DB {
 return func(db *gorm.DB) *gorm.DB {
  if !config.ApplicationConfig.EnableDP {
   return db
  }
  switch p.DataScope {
  case "2":
   return db.Where(tableName+".create_by in (select sys_user.user_id from sys_role_dept left join sys_user on sys_user.dept_id=sys_role_dept.dept_id where sys_role_dept.role_id = ?)", p.RoleId)
  case "3":
   return db.Where(tableName+".create_by in (SELECT user_id from sys_user where dept_id = ? )", p.DeptId)
  case "4":
   return db.Where(tableName+".create_by in (SELECT user_id from sys_user where sys_user.dept_id in(select dept_id from sys_dept where dept_path like ? ))", "%/"+pkg.IntToString(p.DeptId)+"/%")
  case "5":
   return db.Where(tableName+".create_by = ?", p.UserId)
  default:
   return db
  }
 }
}

总结

gorm Scopes是一个非常强大的特性,它可以让你复用你的逻辑,在查询时实现更为复杂的查询逻辑。在使用gorm Scope时,你需要定义一个Scope函数,并在查询时应用它。Scope函数可以被链式调用,并且可以接收参数。学习并掌握这个特性将会使你在编写gorm查询时事半功倍。

以上就是使用gorm.Scopes函数实现复用查询逻辑示例的详细内容,更多关于gorm.Scopes复用查询逻辑的资料请关注脚本之家其它相关文章!

相关文章

  • 一文带你使用Golang实现SSH客户端

    一文带你使用Golang实现SSH客户端

    SSH 全称为 Secure Shell,是一种用于安全地远程登录到网络上的其他计算机的网络协议,本文主要为大家详细介绍了如何使用 Golang 实现 SSH 客户端,需要的可以参考下
    2023-11-11
  • 浅谈Go语言多态的实现与interface使用

    浅谈Go语言多态的实现与interface使用

    如果大家系统的学过C++、Java等语言以及面向对象的话,相信应该对多态不会陌生。多态是面向对象范畴当中经常使用并且非常好用的一个功能,它主要是用在强类型语言当中,像是Python这样的弱类型语言,变量的类型可以随意变化,也没有任何限制,其实区别不是很大
    2021-06-06
  • golang如何利用原始套接字构造UDP包详解

    golang如何利用原始套接字构造UDP包详解

    这篇文章主要给大家介绍了关于golang如何利用原始套接字构造UDP包的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用golang具有一定的参考学习价值,需要的朋友们下面来一起看看吧。
    2017-10-10
  • golang判断key是否在map中的代码

    golang判断key是否在map中的代码

    这篇文章主要介绍了golang判断key是否在map中的代码,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-04-04
  • 基于Go语言实现插入排序算法及优化

    基于Go语言实现插入排序算法及优化

    插入排序是一种简单的排序算法。这篇文章将利用Go语言实现冒泡排序算法,文中的示例代码讲解详细,对学习Go语言有一定的帮助,需要的可以参考一下
    2022-12-12
  • 深入了解Golang为什么需要超时控制

    深入了解Golang为什么需要超时控制

    本文将介绍为什么需要超时控制,然后详细介绍Go语言中实现超时控制的方法,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起了解一下
    2023-05-05
  • golang gorm 操作mysql及gorm基本用法

    golang gorm 操作mysql及gorm基本用法

    golang 官方的那个操作mysql的有点麻烦所以就使用了gorm,下面就gorm的使用做下简单介绍,感兴趣的朋友跟随小编一起看看吧
    2018-11-11
  • Golang与其他语言不同的九个特性

    Golang与其他语言不同的九个特性

    近来关于对Golang的讨论有很多,七牛的几个大牛们也断定Go语言在未来将会快速发展,并且很可能会取代Java成为互联网时代最受欢迎的编程语言。本文将带你了解它不同于其他语言的九个特性
    2021-09-09
  • go语言中的结构体嵌入详解(最新推荐)

    go语言中的结构体嵌入详解(最新推荐)

    结构体嵌入是Go语言中的一种特性,允许在一个结构体中包含另一个结构体,从而实现代码复用和方法复用,嵌入结构体的字段和方法会被提升到外部结构体中,可以直接访问,本文给大家介绍go语言中的结构体嵌入的相关知识,感兴趣的朋友跟随小编一起看看吧
    2025-12-12
  • Golang重构wget脚本构建通用并发下载工具的实践指南

    Golang重构wget脚本构建通用并发下载工具的实践指南

    在当今的开发和运维工作中,大文件批量下载是一个常见需求,本文分享如何用Golang构建一个通用高效的并发下载工具,有需要的小伙伴可以了解下
    2025-11-11

最新评论