使用gorm.Scopes函數(shù)實(shí)現(xiàn)復(fù)用查詢邏輯示例
引言
今天要學(xué)習(xí)的是gorm.Scopes
函數(shù)的使用。該函數(shù)的作用就是復(fù)用查詢條件。
gorm Scopes是什么
在項(xiàng)目中,你一定會(huì)遇到過(guò)很多需要復(fù)用的查詢條件。比如常用的場(chǎng)景有分頁(yè)、查詢時(shí)判定數(shù)據(jù)權(quán)限等操作。
比如,我們有兩個(gè)數(shù)據(jù)資源:用戶列表和部門列表。那么,在查詢列表的時(shí)候都會(huì)涉及到分頁(yè)。當(dāng)然可以在每個(gè)列表中都增加上列表相關(guān)的查詢。同時(shí),也可以將分頁(yè)的查詢抽取出來(lái),做成公共的函數(shù)。
那怎么將抽取出來(lái)的分頁(yè)條件在每個(gè)列表中都能復(fù)用呢?那就是使用gorm.Scopes
函數(shù)。
我們先看一個(gè)使用gorm.Scopes函數(shù)使用的簡(jiǎn)單例子,這個(gè)例子只是為了說(shuō)明gorm.Scopes
函數(shù)的使用。如下:
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 的已付款或已發(fā)貨訂單 db.Scopes(AmountGreaterThan1000, OrderStatus([]string{"paid", "shipped"})).Find(&orders)
通過(guò)上述例子可以知道,前4個(gè)函數(shù)都是func (db *gorm.DB) *gorm.DB
類型的函數(shù),即輸入一個(gè)db,然后返回一個(gè)db。在該函數(shù)中的業(yè)務(wù)邏輯其實(shí)就是最常見(jiàn)的db.Where
、db.Offset
等常用的查詢條件語(yǔ)句而已。只不過(guò)是對(duì)這種公共的查詢語(yǔ)句進(jìn)行了提取并進(jìn)行復(fù)用而已。
然后將這樣的函數(shù)傳遞給Scopes
。Scopes
函數(shù)只是簡(jiǎn)單的將func (db *gorm.DB) *gorm.DB
放到Statement.scopes
這個(gè)切片中。
最后,在最終執(zhí)行的時(shí)候,會(huì)循環(huán)遍歷Statement.scopes
切片,依次執(zhí)行該切片中的每一個(gè)func (db *gorm.DB) *gorm.DB
函數(shù)。這樣,就把提取出來(lái)的公共的查詢條件融合在一起了。
使用場(chǎng)景1 -- 分頁(yè)
當(dāng)然,我們?cè)诓樵儠r(shí)最常用的就是分頁(yè)功能。那么,如何使用gorm.Scopes
實(shí)現(xiàn)分頁(yè)查詢的復(fù)用呢。如下:
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)
你看,先定義了一個(gè)分頁(yè)的函數(shù)Paginate
函數(shù),該函數(shù)接收一個(gè)*http.Request
參數(shù),然后返回一個(gè)func(db *gorm.DB) *gorm.DB
的函數(shù)。因?yàn)?code>gorm.Scopes函數(shù)只接受func(db *gorm.DB) *gorm.DB
類型的函數(shù)。最后,將Paginate
函數(shù)傳遞給Scopes
函數(shù)即可。
使用場(chǎng)景2 -- 數(shù)據(jù)權(quán)限
在go-admin開(kāi)源項(xiàng)目中,我們還發(fā)現(xiàn)了一個(gè)典型的應(yīng)用,就是數(shù)據(jù)權(quán)限。在我們的系統(tǒng)中,會(huì)遇到這樣的場(chǎng)景:一些數(shù)據(jù)只能自己查看或操作;或者你的上級(jí)也能查看或操作;或者同部門的人員能查看或操作自己部門的數(shù)據(jù),但不能查看或操作其他部門的權(quán)限;又或者只能查看同部門的數(shù)據(jù)但不能操作同部門的數(shù)據(jù)等等。
在go-admin中,就使用了gorm.Scopes
函數(shù)來(lái)統(tǒng)一使用權(quán)限查詢條件。在每個(gè)操作中,都通過(guò)Scopes
函數(shù)傳入了一個(gè)Permission
函數(shù)。Permissioin
函數(shù)是根據(jù)不同角色擁有的權(quán)限,轉(zhuǎn)換成對(duì)應(yīng)的sql語(yǔ)句。如下:
// DeleteAction 通用刪除動(dòng)作 func DeleteAction(control dto.Control) gin.HandlerFunc { return func(c *gin.Context) { db, err := pkg.GetOrm(c) ...//省略了一些邏輯 //數(shù)據(jù)權(quán)限檢查 p := GetPermissionFromContext(c) // 將Permission函數(shù)傳入Scopes函數(shù) db = db.WithContext(c).Scopes( Permission(object.TableName(), p), ).Where(req.GetId()).Delete(object) ...//省略了一些邏輯 // 檢查操作的行數(shù),如果操作的行數(shù)是0,說(shuō)明沒(méi)有權(quán)限 if db.RowsAffected == 0 { response.Error(c, http.StatusForbidden, nil, "無(wú)權(quán)刪除該數(shù)據(jù)") return } response.OK(c, object.GetId(), "刪除成功") c.Next() } } // Permission函數(shù)的邏輯 // 根據(jù)不同 的數(shù)據(jù)范圍枚舉值,轉(zhuǎn)換成不同的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 } } }
總結(jié)
gorm Scopes是一個(gè)非常強(qiáng)大的特性,它可以讓你復(fù)用你的邏輯,在查詢時(shí)實(shí)現(xiàn)更為復(fù)雜的查詢邏輯。在使用gorm Scope時(shí),你需要定義一個(gè)Scope函數(shù),并在查詢時(shí)應(yīng)用它。Scope函數(shù)可以被鏈?zhǔn)秸{(diào)用,并且可以接收參數(shù)。學(xué)習(xí)并掌握這個(gè)特性將會(huì)使你在編寫gorm查詢時(shí)事半功倍。
以上就是使用gorm.Scopes函數(shù)實(shí)現(xiàn)復(fù)用查詢邏輯示例的詳細(xì)內(nèi)容,更多關(guān)于gorm.Scopes復(fù)用查詢邏輯的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Go語(yǔ)言實(shí)現(xiàn)的可讀性更高的并發(fā)神庫(kù)詳解
這篇文章主要為大家介紹了Go語(yǔ)言實(shí)現(xiàn)的可讀性更高的并發(fā)神庫(kù)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-01-01Go 語(yǔ)言中靜態(tài)類型和動(dòng)態(tài)類型的使用
本文主要介紹了Go語(yǔ)言中的靜態(tài)類型和動(dòng)態(tài)類型,靜態(tài)類型在編譯時(shí)確定,提供了類型安全,性能優(yōu)化和代碼清晰,而動(dòng)態(tài)類型在運(yùn)行時(shí)確定,提供了更高的靈活性,但可能引發(fā)運(yùn)行時(shí)錯(cuò)誤,下面就來(lái)介紹一下,感興趣的可以了解一下2024-10-10golang控制結(jié)構(gòu)select機(jī)制及使用示例詳解
這篇文章主要介紹了golang控制結(jié)構(gòu)select機(jī)制及使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-10-10Go 數(shù)據(jù)結(jié)構(gòu)之堆排序示例詳解
這篇文章主要為大家介紹了Go 數(shù)據(jù)結(jié)構(gòu)之堆排序示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-08-08Go 控制協(xié)程(goroutine)的并發(fā)數(shù)量
控制協(xié)程goroutine的并發(fā)數(shù)量是一個(gè)常見(jiàn)的需求,本文就來(lái)介紹一下Go 控制協(xié)程的并發(fā)數(shù)量,具有一定的參考價(jià)值,感興趣的可以了解一下2025-02-02Golang?Compare?And?Swap算法詳細(xì)介紹
CAS算法是一種有名的無(wú)鎖算法。無(wú)鎖編程,即不使用鎖的情況下實(shí)現(xiàn)多線程之間的變量同步,也就是在沒(méi)有線程被阻塞的情況下實(shí)現(xiàn)變量的同步,所以也叫非阻塞同步Non-blocking?Synchronization2022-10-10一文深入探索Go語(yǔ)言中的循環(huán)結(jié)構(gòu)
在編程中,循環(huán)結(jié)構(gòu)扮演著重要的角色,它使我們能夠有效地重復(fù)執(zhí)行特定的代碼塊,以實(shí)現(xiàn)各種任務(wù)和邏輯,在Go語(yǔ)言中,for 是 Go 中唯一的循環(huán)結(jié)構(gòu),本文將深入探討Go語(yǔ)言中的for循環(huán)類型以及它們的用法2023-08-08Go創(chuàng)建Grpc鏈接池實(shí)現(xiàn)過(guò)程詳解
這篇文章主要為大家介紹了Go創(chuàng)建Grpc鏈接池實(shí)現(xiàn)過(guò)程詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03xorm根據(jù)數(shù)據(jù)庫(kù)生成go model文件的操作
這篇文章主要介紹了xorm根據(jù)數(shù)據(jù)庫(kù)生成go model文件的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-12-12