使用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.Where
、db.Offset
等常用的查询条件语句而已。只不过是对这种公共的查询语句进行了提取并进行复用而已。
然后将这样的函数传递给Scopes
。Scopes
函数只是简单的将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中使用Sync.WaitGroup解决等待的问题
这篇文章主要介绍了在golang中使用Sync.WaitGroup解决等待的问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧2021-04-04
最新评论