Gin+Gorm實(shí)現(xiàn)CRUD的實(shí)戰(zhàn)
簡介:
Q:Gin和Gorm都是干什么的?有什么區(qū)別?
A:Gin 和 Gorm 是 Go 編程語言中流行的開源庫。但是,它們服務(wù)于不同的目的,通常在 web 開發(fā)項(xiàng)目中一起使用。
Gin 是一個(gè)用于構(gòu)建 HTTP 服務(wù)器的 web 框架。它提供了一個(gè)簡單易用的 API,用于處理 HTTP 請求和響應(yīng)、路由、中間件和其他常見的 web 應(yīng)用程序所需的功能。它以其高性能和簡約為特點(diǎn),提供了輕量級和靈活的解決方案來構(gòu)建 web 服務(wù)器。
Gorm 是 Go 的一個(gè) ORM(對象關(guān)系映射)庫。它提供了一個(gè)簡單易用的 API,用于與數(shù)據(jù)庫交互、處理數(shù)據(jù)庫遷移和執(zhí)行常見的數(shù)據(jù)庫操作,如查詢、插入、更新和刪除記錄。它支持多種數(shù)據(jù)庫后端,包括 MySQL、PostgreSQL、SQLite 等。
總而言之, Gin 是用于處理 HTTP 請求和響應(yīng)、路由、中間件和其他與網(wǎng)絡(luò)相關(guān)的東西的 web 框架,而 Gorm 則是用于與數(shù)據(jù)庫交互并執(zhí)行常見數(shù)據(jù)庫操作的 ORM 庫。它們通常一起使用,來處理 HTTP 請求/響應(yīng)并在 web 開發(fā)項(xiàng)目中存儲或獲取數(shù)據(jù)。
開發(fā)環(huán)境:
- Windows 10
- VSCode
一、Gin
0. 快速入門:
package main import ( "encoding/json" "net/http" "github.com/gin-gonic/gin" "github.com/thinkerou/favicon" ) // 中間件(攔截器),功能:預(yù)處理,登錄授權(quán)、驗(yàn)證、分頁、耗時(shí)統(tǒng)計(jì)... // func myHandler() gin.HandlerFunc { // return func(ctx *gin.Context) { // // 通過自定義中間件,設(shè)置的值,在后續(xù)處理只要調(diào)用了這個(gè)中間件的都可以拿到這里的參數(shù) // ctx.Set("usersesion", "userid-1") // ctx.Next() // 放行 // ctx.Abort() // 阻止 // } // } func main() { // 創(chuàng)建一個(gè)服務(wù) ginServer := gin.Default() ginServer.Use(favicon.New("./Arctime.ico")) // 這里如果添加了東西然后再運(yùn)行沒有變化,請重啟瀏覽器,瀏覽器有緩存 // 加載靜態(tài)頁面 ginServer.LoadHTMLGlob("templates/*") // 一種是全局加載,一種是加載指定的文件 // 加載資源文件 ginServer.Static("/static", "./static") // 相應(yīng)一個(gè)頁面給前端 ginServer.GET("/index", func(ctx *gin.Context) { ctx.HTML(http.StatusOK, "index.html", gin.H{ "msg": "This data is come from Go background.", }) }) // 能加載靜態(tài)頁面也可以加載測試文件 // 獲取請求中的參數(shù) // 傳統(tǒng)方式:usl?userid=xxx&username=conqueror712 // Rustful方式:/user/info/1/conqueror712 // 下面是傳統(tǒng)方式的例子 ginServer.GET("/user/info", func(context *gin.Context) { // 這個(gè)格式是固定的 userid := context.Query("userid") username := context.Query("username") // 拿到之后返回給前端 context.JSON(http.StatusOK, gin.H{ "userid": userid, "username": username, }) }) // 此時(shí)執(zhí)行代碼之后,在瀏覽器中可以輸入http://localhost:8081/user/info?userid=111&username=666 // 就可以看到返回了JSON格式的數(shù)據(jù) // 下面是Rustful方式的例子 ginServer.GET("/user/info/:userid/:username", func(context *gin.Context) { userid := context.Param("userid") username := context.Param("username") // 還是一樣,返回給前端 context.JSON(http.StatusOK, gin.H{ "userid": userid, "username": username, }) }) // 指定代碼后,只需要在瀏覽器中http://localhost:8081/user/info/111/555 // 就可以看到返回了JSON數(shù)據(jù)了,非常方便簡潔 // 序列化 // 前端給后端傳遞JSON ginServer.POST("/json", func(ctx *gin.Context) { // request.body data, _ := ctx.GetRawData() var m map[string]interface{} // Go語言中object一般用空接口來表示,可以接收anything // 順帶一提,1.18以上,interface可以直接改成any _ = json.Unmarshal(data, &m) ctx.JSON(http.StatusOK, m) }) // 用apipost或者postman寫一段json傳到localhost:8081/json里就可以了 /* json示例: { "name": "Conqueror712", "age": 666, "address": "Mars" } */ // 看到后端的實(shí)時(shí)響應(yīng)里面接收到數(shù)據(jù)就可以了 // 處理表單請求 這些都是支持函數(shù)式編程,Go語言特性,可以把函數(shù)作為參數(shù)傳進(jìn)來 ginServer.POST("/user/add", func(ctx *gin.Context) { username := ctx.PostForm("username") password := ctx.PostForm("password") ctx.JSON(http.StatusOK, gin.H{ "msg": "ok", "username": username, "password": password, }) }) // 路由 ginServer.GET("/test", func(ctx *gin.Context) { // 重定向 -> 301 ctx.Redirect(301, "https://conqueror712.gitee.io/conqueror712.gitee.io/") }) // http://localhost:8081/test // 404 ginServer.NoRoute(func(ctx *gin.Context) { ctx.HTML(404, "404.html", nil) }) // 路由組暫略 // 服務(wù)器端口,用服務(wù)器端口來訪問地址 ginServer.Run(":8081") // 不寫的話默認(rèn)是8080,也可以更改 }
API用法示例:https://gin-gonic.com/zh-cn/docs/examples/
1. 基準(zhǔn)測試
Q:基準(zhǔn)測試是什么?
A:基準(zhǔn)測試,也稱為性能測試或壓力測試,是一種用于測量系統(tǒng)或組件性能的測試?;鶞?zhǔn)測試的目的是了解系統(tǒng)或組件在特定條件下的性能,并將結(jié)果與其他類似系統(tǒng)或組件進(jìn)行比較?;鶞?zhǔn)測試可用于評估各種類型的系統(tǒng)和組件,包括硬件、軟件、網(wǎng)絡(luò)和整個(gè)系統(tǒng)。
Q:什么時(shí)候需要基準(zhǔn)測試呀?
A:基準(zhǔn)測試通常涉及在被測系統(tǒng)或組件上運(yùn)行特定工作負(fù)載或任務(wù),并測量吞吐量、延遲時(shí)間、CPU使用率、內(nèi)存使用率等各種性能指標(biāo)?;鶞?zhǔn)測試的結(jié)果可用于識別瓶頸和性能問題,并做出有關(guān)如何優(yōu)化系統(tǒng)或組件以提高性能的明智決策。
有許多不同類型的基準(zhǔn)測試,每種類型都有自己的指標(biāo)和工作負(fù)載。常見的基準(zhǔn)測試類型包括:
- 人工基準(zhǔn)測試:使用人工工作負(fù)載來測量系統(tǒng)或組件的性能。
- 真實(shí)世界基準(zhǔn)測試:使用真實(shí)世界的工作負(fù)載或場景來測量系統(tǒng)或組件的性能。
- 壓力測試:旨在將系統(tǒng)或組件推到極限,以確定在正常使用條件下可能不明顯的性能問題
重要的是要知道基準(zhǔn)測試不是一次性的活動,而是應(yīng)該定期進(jìn)行的活動,以評估系統(tǒng)的性能并檢測隨時(shí)間的消耗。
Q:什么樣的基準(zhǔn)測試結(jié)果是我們想要的呀?
A:
- 在一定的時(shí)間內(nèi)實(shí)現(xiàn)的總調(diào)用數(shù),越高越好
- 單次操作耗時(shí)(ns/op),越低越好
- 堆內(nèi)存分配 (B/op), 越低越好
- 每次操作的平均內(nèi)存分配次數(shù)(allocs/op),越低越好
2. Gin的特性與Jsoniter:
Gin v1 穩(wěn)定的特性:
- 零分配路由。
- 仍然是最快的 http 路由器和框架。
- 完整的單元測試支持。
- 實(shí)戰(zhàn)考驗(yàn)。
- API 凍結(jié),新版本的發(fā)布不會破壞你的代碼。
- Gin 項(xiàng)目可以輕松部署在任何云提供商上。
Gin 使用 encoding/json 作為默認(rèn)的 json 包,但是你可以在編譯中使用標(biāo)簽將其修改為 jsoniter。
$ go build -tags=jsoniter .
Jsoniter是什么?
json-iterator
是一款快且靈活的JSON
解析器,同時(shí)提供Java
和Go
兩個(gè)版本。json-iterator
是最快的JSON
解析器。它最多能比普通的解析器快10倍之多- 獨(dú)特的
iterator api
能夠直接遍歷JSON
,極致性能、零內(nèi)存分配 - 從dsljson和jsonparser借鑒了大量代碼。
下載依賴:go get github.com/json-iterator/go
二、GORM
0. 特性與安裝:
- 全功能 ORM
- 關(guān)聯(lián) (Has One,Has Many,Belongs To,Many To Many,多態(tài),單表繼承)
- Create,Save,Update,Delete,F(xiàn)ind 中鉤子方法
- 支持
Preload
、Joins
的預(yù)加載 - 事務(wù),嵌套事務(wù),Save Point,Rollback To Saved Point
- Context、預(yù)編譯模式、DryRun 模式
- 批量插入,F(xiàn)indInBatches,F(xiàn)ind/Create with Map,使用 SQL 表達(dá)式、Context Valuer 進(jìn)行 CRUD
- SQL 構(gòu)建器,Upsert,數(shù)據(jù)庫鎖,Optimizer/Index/Comment Hint,命名參數(shù),子查詢
- 復(fù)合主鍵,索引,約束
- Auto Migration
- 自定義 Logger
- 靈活的可擴(kuò)展插件 API:Database Resolver(多數(shù)據(jù)庫,讀寫分離)、Prometheus…
- 每個(gè)特性都經(jīng)過了測試的重重考驗(yàn)
- 開發(fā)者友好
go get -u gorm.io/gorm go get -u gorm.io/driver/sqlite
其他的補(bǔ)充內(nèi)容:
- Gorm是軟刪除,為了保證數(shù)據(jù)庫的完整性
三、Navicat
新建連接 -> MySQL -> 連接名隨便 -> 密碼隨便 -> 雙擊左側(cè)打開 -> 右鍵information_schema -> 新建數(shù)據(jù)庫 -> 名稱crud-list -> 字符集utf8mb4
這里如果打開的時(shí)候報(bào)錯(cuò)navicat 1045 - access denied for user 'root'@'localhost' (using password: 'YES')
,則需要查看自己的數(shù)據(jù)庫本身的問題
四、Gin+Gorm的CRUD
連接數(shù)據(jù)庫
編寫測試代碼,成功運(yùn)行即可,但是這個(gè)時(shí)候還不能查看數(shù)據(jù)庫是否被創(chuàng)建,
如果要看我們需要定義結(jié)構(gòu)體,然后定義表遷移,具體代碼如下:
package main import ( "fmt" "time" // "gorm.io/driver/sqlite" "github.com/gin-gonic/gin" "gorm.io/driver/mysql" "gorm.io/gorm" ) func main() { // 如何連接數(shù)據(jù)庫 ? MySQL + Navicat // 需要更改的內(nèi)容:用戶名,密碼,數(shù)據(jù)庫名稱 dsn := "root:BqV?eGcc_1o+@tcp(127.0.0.1:3306)/crud-list?charset=utf8mb4&parseTime=True&loc=Local" db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{}) fmt.Println("db = ", db) fmt.Println("err = ", err) // 連接池 sqlDB, err := db.DB() // SetMaxIdleConns 設(shè)置空閑連接池中連接的最大數(shù)量 sqlDB.SetMaxIdleConns(10) // SetMaxOpenConns 設(shè)置打開數(shù)據(jù)庫連接的最大數(shù)量。 sqlDB.SetMaxOpenConns(100) // SetConnMaxLifetime 設(shè)置了連接可復(fù)用的最大時(shí)間。 sqlDB.SetConnMaxLifetime(10 * time.Second) // 10秒鐘 // 結(jié)構(gòu)體 type List struct { Name string State string Phone string Email string Address string } // 遷移 db.AutoMigrate(&List{}) // 接口 r := gin.Default() // 端口號 PORT := "3001" r.Run(":" + PORT) }
定義好之后我們運(yùn)行,沒有報(bào)錯(cuò)并且在終端顯示出來3001就是正確的,這個(gè)時(shí)候我們可以去Navicat里面查看crud-list下面的"表",刷新后發(fā)現(xiàn)有一個(gè)lists產(chǎn)生,那就是對的了。
但是這個(gè)時(shí)候我們存在兩個(gè)問題:
- 沒有主鍵:在struct里添加
gorm.Model
來解決,Ctrl+左鍵可以查看model
- 表里面的名稱變成了復(fù)數(shù):詳見文檔的高級主題-GORM配置里,在
*db*, *err* *:=* gorm.Open(mysql.Open(dsn), *&*gorm.Config{})
里面添加一段話即可
更改完成之后我們要先在Navicat里面把原來的表lists
刪掉才能重新創(chuàng)建,這個(gè)時(shí)候我們重新運(yùn)行,就會發(fā)現(xiàn)表單里面多了很多東西
結(jié)構(gòu)體定義與優(yōu)化
例如:
`gorm:"type:varchar(20); not null" json:"name" binding:"required"`
需要注意的是:
- 結(jié)構(gòu)體里面的變量(Name)必須首字母大寫,否則創(chuàng)建不出列,會被自動忽略
- gorm指定類型
- json表示json接收的時(shí)候的名稱
- binding required表示必須傳入
CRUD接口
// 測試 r.GET("/", func(c *gin.Context) { c.JSON(200, gin.H{ "message": "請求成功", }) })
編寫完這一段之后運(yùn)行代碼,然后去postman里面新建一個(gè)GET接口127.0.0.1:3001
然后send一下,出現(xiàn)請求成功就請求成功了。
增也是一樣,寫好之后直接用如下JSON來測試就可以:
{ "name" : "張三", "state" : "在職", "phone" : "13900000000", "email" : "6666@qq.com", "address" : "二仙橋成華大道" }
返回:
{ "code": "200", "data": { "ID": 1, "CreatedAt": "2023-01-24T09:27:36.73+08:00", "UpdatedAt": "2023-01-24T09:27:36.73+08:00", "DeletedAt": null, "name": "張三", "state": "在職", "phone": "13900000000", "email": "6666@qq.com", "address": "二仙橋成華大道" }, "msg": "添加成功" }
這時(shí)候也可以在數(shù)據(jù)庫里看到這條數(shù)據(jù)
刪除也是一樣,編寫完運(yùn)行之后添加一個(gè)DELETE接口,然后輸入127.0.0.1:3001/user/delete/2
之后send就可以看到返回了(前提是有數(shù)據(jù))
{ "code": 200, "msg": "刪除成功" }
如果是刪除了不存在的id,就會返回
{ "code": 400, "msg": "id沒有找到,刪除失敗" }
順帶一提,事實(shí)上這個(gè)代碼還可以優(yōu)化,這里的if else太多了,后面優(yōu)化的時(shí)候有錯(cuò)誤直接return
修改也是一樣,示例:
{ "name" : "張三", "state" : "離職", "phone" : "13900000000", "email" : "6666@qq.com", "address" : "二仙橋成華大道" }
返回:
{ "code": 200, "msg": "修改成功" }
查詢分為兩種:
- 條件查詢
- 分頁查詢
條件查詢的話,直接寫好了請求127.0.0.1:3001/user/list/王五
返回:
{ "code": "200", "data": [ { "ID": 3, "CreatedAt": "2023-01-24T10:06:25.305+08:00", "UpdatedAt": "2023-01-24T10:06:25.305+08:00", "DeletedAt": null, "name": "王五", "state": "在職", "phone": "13100000000", "email": "8888@qq.com", "address": "八仙橋成華大道" } ], "msg": "查詢成功" }
全部 / 分頁查詢的話
譬如說請求是:127.0.0.1:3001/user/list?pageNum=1&pageSize=2
意思就是查詢第一頁的兩個(gè)
返回:
{ "code": 200, "data": { "list": [ { "ID": 3, "CreatedAt": "2023-01-24T10:06:25.305+08:00", "UpdatedAt": "2023-01-24T10:06:25.305+08:00", "DeletedAt": null, "name": "王五", "state": "在職", "phone": "13100000000", "email": "8888@qq.com", "address": "八仙橋成華大道" } ], "pageNum": 1, "pageSize": 2, "total": 2 }, "msg": "查詢成功" }
如果請求是:127.0.0.1:3001/user/list
返回:
{ "code": 200, "data": { "list": [ { "ID": 1, "CreatedAt": "2023-01-24T09:27:36.73+08:00", "UpdatedAt": "2023-01-24T09:55:20.351+08:00", "DeletedAt": null, "name": "張三", "state": "離職", "phone": "13900000000", "email": "6666@qq.com", "address": "二仙橋成華大道" }, { "ID": 3, "CreatedAt": "2023-01-24T10:06:25.305+08:00", "UpdatedAt": "2023-01-24T10:06:25.305+08:00", "DeletedAt": null, "name": "王五", "state": "在職", "phone": "13100000000", "email": "8888@qq.com", "address": "八仙橋成華大道" } ], "pageNum": 0, "pageSize": 0, "total": 2 }, "msg": "查詢成功" }
完整代碼如下:
package main import ( "fmt" "strconv" "time" // "gorm.io/driver/sqlite" "github.com/gin-gonic/gin" "gorm.io/driver/mysql" "gorm.io/gorm" "gorm.io/gorm/schema" ) func main() { // 如何連接數(shù)據(jù)庫 ? MySQL + Navicat // 需要更改的內(nèi)容:用戶名,密碼,數(shù)據(jù)庫名稱 dsn := "root:password@tcp(127.0.0.1:3306)/database?charset=utf8mb4&parseTime=True&loc=Local" db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{ NamingStrategy: schema.NamingStrategy{ SingularTable: true, }, }) fmt.Println("db = ", db) fmt.Println("err = ", err) // 連接池 sqlDB, err := db.DB() // SetMaxIdleConns 設(shè)置空閑連接池中連接的最大數(shù)量 sqlDB.SetMaxIdleConns(10) // SetMaxOpenConns 設(shè)置打開數(shù)據(jù)庫連接的最大數(shù)量。 sqlDB.SetMaxOpenConns(100) // SetConnMaxLifetime 設(shè)置了連接可復(fù)用的最大時(shí)間。 sqlDB.SetConnMaxLifetime(10 * time.Second) // 10秒鐘 // 結(jié)構(gòu)體 type List struct { gorm.Model // 主鍵 Name string `gorm:"type:varchar(20); not null" json:"name" binding:"required"` State string `gorm:"type:varchar(20); not null" json:"state" binding:"required"` Phone string `gorm:"type:varchar(20); not null" json:"phone" binding:"required"` Email string `gorm:"type:varchar(40); not null" json:"email" binding:"required"` Address string `gorm:"type:varchar(200); not null" json:"address" binding:"required"` } // 遷移 db.AutoMigrate(&List{}) // 接口 r := gin.Default() // 測試 // r.GET("/", func(c *gin.Context) { // c.JSON(200, gin.H{ // "message": "請求成功", // }) // }) // 業(yè)務(wù)碼約定:正確200,錯(cuò)誤400 // 增 r.POST("/user/add", func(ctx *gin.Context) { // 定義一個(gè)變量指向結(jié)構(gòu)體 var data List // 綁定方法 err := ctx.ShouldBindJSON(&data) // 判斷綁定是否有錯(cuò)誤 if err != nil { ctx.JSON(200, gin.H{ "msg": "添加失敗", "data": gin.H{}, "code": "400", }) } else { // 數(shù)據(jù)庫的操作 db.Create(&data) // 創(chuàng)建一條數(shù)據(jù) ctx.JSON(200, gin.H{ "msg": "添加成功", "data": data, "code": "200", }) } }) // 刪 // 1. 找到對應(yīng)的id對應(yīng)的條目 // 2. 判斷id是否存在 // 3. 從數(shù)據(jù)庫中刪除 or 返回id沒有找到 // Restful編碼規(guī)范 r.DELETE("/user/delete/:id", func(ctx *gin.Context) { var data []List // 接收id id := ctx.Param("id") // 如果有鍵值對形式的話用Query() // 判斷id是否存在 db.Where("id = ? ", id).Find(&data) if len(data) == 0 { ctx.JSON(200, gin.H{ "msg": "id沒有找到,刪除失敗", "code": 400, }) } else { // 操作數(shù)據(jù)庫刪除(刪除id所對應(yīng)的那一條) // db.Where("id = ? ", id).Delete(&data) <- 其實(shí)不需要這樣寫,因?yàn)椴榈降膁ata里面就是要刪除的數(shù)據(jù) db.Delete(&data) ctx.JSON(200, gin.H{ "msg": "刪除成功", "code": 200, }) } }) // 改 r.PUT("/user/update/:id", func(ctx *gin.Context) { // 1. 找到對應(yīng)的id所對應(yīng)的條目 // 2. 判斷id是否存在 // 3. 修改對應(yīng)條目 or 返回id沒有找到 var data List id := ctx.Param("id") // db.Where("id = ?", id).Find(&data) 可以這樣寫,也可以寫成下面那樣 // 還可以再Where后面加上Count函數(shù),可以查出來這個(gè)條件對應(yīng)的條數(shù) db.Select("id").Where("id = ? ", id).Find(&data) if data.ID == 0 { ctx.JSON(200, gin.H{ "msg": "用戶id沒有找到", "code": 400, }) } else { // 綁定一下 err := ctx.ShouldBindJSON(&data) if err != nil { ctx.JSON(200, gin.H{ "msg": "修改失敗", "code": 400, }) } else { // db修改數(shù)據(jù)庫內(nèi)容 db.Where("id = ?", id).Updates(&data) ctx.JSON(200, gin.H{ "msg": "修改成功", "code": 200, }) } } }) // 查 // 第一種:條件查詢, r.GET("/user/list/:name", func(ctx *gin.Context) { // 獲取路徑參數(shù) name := ctx.Param("name") var dataList []List // 查詢數(shù)據(jù)庫 db.Where("name = ? ", name).Find(&dataList) // 判斷是否查詢到數(shù)據(jù) if len(dataList) == 0 { ctx.JSON(200, gin.H{ "msg": "沒有查詢到數(shù)據(jù)", "code": "400", "data": gin.H{}, }) } else { ctx.JSON(200, gin.H{ "msg": "查詢成功", "code": "200", "data": dataList, }) } }) // 第二種:全部查詢 / 分頁查詢 r.GET("/user/list", func(ctx *gin.Context) { var dataList []List // 查詢?nèi)繑?shù)據(jù) or 查詢分頁數(shù)據(jù) pageSize, _ := strconv.Atoi(ctx.Query("pageSize")) pageNum, _ := strconv.Atoi(ctx.Query("pageNum")) // 判斷是否需要分頁 if pageSize == 0 { pageSize = -1 } if pageNum == 0 { pageNum = -1 } offsetVal := (pageNum - 1) * pageSize // 固定寫法 記住就行 if pageNum == -1 && pageSize == -1 { offsetVal = -1 } // 返回一個(gè)總數(shù) var total int64 // 查詢數(shù)據(jù)庫 db.Model(dataList).Count(&total).Limit(pageSize).Offset(offsetVal).Find(&dataList) if len(dataList) == 0 { ctx.JSON(200, gin.H{ "msg": "沒有查詢到數(shù)據(jù)", "code": 400, "data": gin.H{}, }) } else { ctx.JSON(200, gin.H{ "msg": "查詢成功", "code": 200, "data": gin.H{ "list": dataList, "total": total, "pageNum": pageNum, "pageSize": pageSize, }, }) } }) // 端口號 PORT := "3001" r.Run(":" + PORT) }
到此這篇關(guān)于Gin+Gorm實(shí)戰(zhàn)CRUD的文章就介紹到這了,更多相關(guān)Gin Gorm CRUD內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
golang time包做時(shí)間轉(zhuǎn)換操作
這篇文章主要介紹了golang time包做時(shí)間轉(zhuǎn)換操作,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-12-12使用golang腳本基于kubeadm創(chuàng)建新的token(問題分析)
這篇文章主要介紹了使用golang腳本基于kubeadm創(chuàng)建新的token(問題分析),本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-10-10詳解Golang如何實(shí)現(xiàn)支持隨機(jī)刪除元素的堆
堆是一種非常常用的數(shù)據(jù)結(jié)構(gòu),它能夠支持在O(1)的時(shí)間復(fù)雜度獲取到最大值(或最小值)。本文主要介紹了如何實(shí)現(xiàn)支持O(log(n))隨機(jī)刪除元素的堆,需要的可以參考一下2022-09-09Go的gin參數(shù)校驗(yàn)中的validator庫詳解
這篇文章主要介紹了Go的gin參數(shù)校驗(yàn)之validator庫,使用 validator 以后,只需要在定義結(jié)構(gòu)體時(shí)使用 binding 或 validate tag標(biāo)識相關(guān)校驗(yàn)規(guī)則,就可以進(jìn)行參數(shù)校驗(yàn)了,而不用自己單獨(dú)去寫常見的校驗(yàn)規(guī)則,需要的朋友可以參考下2023-08-08