GO中GORM 使用教程
GORM 是一個用于 Go 語言的 ORM(Object-Relational Mapping) 庫。它將關系型數(shù)據(jù)庫的表與 Go 語言中的結構體相映射,允許開發(fā)者以面向對象的方式操作數(shù)據(jù)庫,而不需要直接編寫 SQL 語句。通過 GORM,開發(fā)者可以利用 Go 語言的結構體和方法來執(zhí)行常見的數(shù)據(jù)庫操作(如查詢、插入、更新、刪除等),大大簡化了與數(shù)據(jù)庫的交互過程。
1. 安裝 GORM
首先,你需要安裝 GORM 庫。打開終端并運行以下命令:
go get -u gorm.io/gorm go get -u gorm.io/driver/sqlite # 示例數(shù)據(jù)庫,可以根據(jù)需求更換為其他數(shù)據(jù)庫驅動
2. 創(chuàng)建基礎結構體
ORM的一個核心概念是結構體,它代表數(shù)據(jù)庫表的一個映射。例如,假設你有一個“用戶”表,我們可以創(chuàng)建一個 User 結構體來表示它。
package main
import (
"gorm.io/driver/sqlite"
"gorm.io/gorm"
)
type User struct {
ID uint `gorm:"primaryKey"` // 主鍵
Name string `gorm:"size:100"` // 用戶名字段,限制長度為100
Age uint // 用戶年齡
CreatedAt time.Time // 創(chuàng)建時間(GORM 會自動管理)
}
func main() {
// 創(chuàng)建數(shù)據(jù)庫連接
db, err := gorm.Open(sqlite.Open("gorm.db"), &gorm.Config{})
if err != nil {
panic("failed to connect to the database")
}
// 自動遷移:通過GORM根據(jù)結構體自動創(chuàng)建表
db.AutoMigrate(&User{})
}
在 GORM 中,結構體字段的標簽(tags)用于定義和控制如何將 Go 結構體映射到數(shù)據(jù)庫表的列。GORM 支持很多標簽,可以配置數(shù)據(jù)庫表的列屬性、索引、關系等。以下是常見的 GORM 字段標簽和它們的作用:
| 標簽 | 描述 | 示例 |
|---|---|---|
| primaryKey | 指定字段為主鍵。 | gorm:"primaryKey" |
| size | 指定字段的大小,通常用于字符串字段。 | gorm:"size:100" |
| unique | 創(chuàng)建唯一索引,確保字段值唯一。 | gorm:"unique" |
| not null | 指定字段不能為空。 | gorm:"not null" |
| default | 指定字段的默認值。 | gorm:"default:0" |
| autoIncrement | 設置字段為自增字段。通常用于整數(shù)類型的主鍵。 | gorm:"autoIncrement" |
| index | 為字段創(chuàng)建索引。 | gorm:"index" |
| uniqueIndex | 為字段創(chuàng)建唯一索引。 | gorm:"uniqueIndex" |
| index:<name> | 創(chuàng)建帶有自定義名稱的索引。 | gorm:"index:idx_name" |
| foreignKey | 指定外鍵字段,在一對多或多對多關系中使用。 | gorm:"foreignKey:UserID" |
| references | 指定外鍵關系中引用的字段。 | gorm:"references:ID" |
| embedded | 嵌入一個結構體,扁平化嵌入的結構體字段到父結構體中。 | gorm:"embedded" |
| preload | 在查詢時預加載關聯(lián)數(shù)據(jù)。 | gorm:"preload" |
| createdAt | 自動生成的創(chuàng)建時間戳字段。 | gorm:"autoCreateTime" |
| updatedAt | 自動生成的更新時間戳字段。 | gorm:"autoUpdateTime" |
| deletedAt | 自動生成的刪除時間戳字段,支持軟刪除。 | gorm:"softDelete" |
| softDelete | 用于支持軟刪除功能。通常與 DeletedAt 配合使用。 | gorm:"index" |
| column | 指定數(shù)據(jù)庫中的列名,當字段名與列名不一致時使用。 | gorm:"column:db_column_name" |
| type | 指定字段在數(shù)據(jù)庫中的類型,通常用于特殊類型(如 JSON)。 | gorm:"type:text" |
| many2many | 用于多對多關系,指定連接表的名稱。 | gorm:"many2many:user_posts" |
這個表格展示了 GORM 中常用的字段標簽以及它們如何影響數(shù)據(jù)庫表結構,幫助開發(fā)者更好地理解和使用 GORM 進行數(shù)據(jù)庫操作。
3. 數(shù)據(jù)庫連接與配置
在上面的示例中,我們使用了 gorm.Open() 來連接 SQLite 數(shù)據(jù)庫。如果你使用 MySQL 或 Postgres 等其他數(shù)據(jù)庫,可以更換相應的驅動。
例如,連接到 MySQL 數(shù)據(jù)庫的代碼如下:
db, err := gorm.Open(mysql.Open("user:password@tcp(localhost:3306)/dbname"), &gorm.Config{})
4. 數(shù)據(jù)庫操作
4.1 創(chuàng)建記錄
你可以使用 Create 方法來插入數(shù)據(jù)。下面是如何插入一個新用戶:
func createUser(db *gorm.DB) {
user := User{Name: "John Doe", Age: 30}
result := db.Create(&user)
if result.Error != nil {
panic("Failed to create user")
}
fmt.Println("User created:", user.ID)
}
代碼中db.Create(&user) 使用 & 符號是因為我們要傳遞結構體 user 的 指針 給 Create 方法。具體來說,這樣做有以下幾個原因:
指針傳遞可以修改原結構體
GORM 的
Create方法接受結構體的 指針,這樣它可以直接修改原始結構體的值,而不僅僅是副本。通過傳遞指針,GORM 能夠在插入數(shù)據(jù)庫的過程中修改結構體(例如,給結構體的字段賦值,例如數(shù)據(jù)庫自動生成的ID或CreatedAt字段),確保結構體反映數(shù)據(jù)庫中的最新數(shù)據(jù)。例如,
user的ID字段會在插入數(shù)據(jù)庫時由 GORM 自動賦值(通常是自增的主鍵),如果你傳遞的是結構體的指針,Create方法可以直接更新user結構體中的ID字段。避免復制大結構體
如果你傳遞的是結構體的副本,GORM 會先創(chuàng)建一個結構體的拷貝并將其插入數(shù)據(jù)庫。這對于較大的結構體來說可能會浪費內存并降低性能。而傳遞指針避免了復制整個結構體,只是傳遞了結構體的內存地址,性能更高。
GORM 的工作方式
GORM 內部使用了指針來標識結構體字段的變化。通過傳遞指針,GORM 可以確定結構體的變化并進行相應的處理。例如,在執(zhí)行
Create時,GORM 會檢查結構體的指針,判斷該字段是否已經賦值、是否需要自動填充等。
4.2 查詢記錄
GORM 提供了多種查詢方式,可以通過結構體查詢、條件查詢等方式來獲取數(shù)據(jù)。
獲取單條記錄
func getUser(db *gorm.DB) {
var user User
result := db.First(&user, 1) // 查找 user 表中主鍵為 1 的記錄,并將其填充到 user 結構體中
if result.Error != nil {
panic("User not found")
}
fmt.Println("User:", user)
}
db.First 是 GORM 提供的一個查詢方法,用于從數(shù)據(jù)庫中獲取 第一條 滿足條件的記錄。它通常用于根據(jù)主鍵或其他條件查詢數(shù)據(jù)。
db.First 的基本語法:
db.First(&model, conditions...)
&model是一個指針參數(shù),表示查詢的結果將會填充到這個結構體中。conditions...是查詢的條件,可以是主鍵或其他字段。
如果查詢成功,db.First 會把查詢到的記錄填充到 model 指針所指向的結構體里。如果沒有找到記錄,它會返回一個錯誤。
在 db.First(&user, 1) 中,&user 是指向 user 結構體的指針。這里傳遞指針是因為 GORM 要修改 user 結構體的值(即填充查詢結果)。
- 通過傳遞結構體的指針,GORM 可以將查詢結果直接賦值到
user結構體中。 - 如果你傳遞的是結構體本身,而不是指針,查詢結果將不會填充到結構體中,因為結構體會作為副本傳遞到
db.First方法,而 GORM 需要能夠修改原始結構體的字段值。
獲取多個記錄
func getUsers(db *gorm.DB) {
var users []User
result := db.Find(&users)
if result.Error != nil {
panic("No users found")
}
fmt.Println("Users:", users)
}
db.Find 是 GORM 提供的查詢方法之一,用于查找多個記錄并將其存儲到傳入的切片結構體中。
Find方法會根據(jù)傳入的條件來查找記錄,可以是簡單的查詢(如所有記錄),也可以是有條件的查詢(如按字段值過濾)。- 傳遞給
Find的參數(shù)是一個指針,它會將查詢到的記錄填充到指向的切片中。
db.Find(&users) 會從數(shù)據(jù)庫中查找所有記錄(或者根據(jù)傳入的查詢條件查找記錄)并將它們填充到 users 切片中。查詢的結果會是一個結構體的集合。Find 方法默認返回所有滿足條件的記錄。
- 如果查詢沒有條件,
Find將返回數(shù)據(jù)庫表中的所有記錄。 - 如果你傳遞了查詢條件,
Find將根據(jù)條件過濾結果。
Find 方法的其他功能
- 查詢條件:你可以通過傳遞查詢條件來限制查詢的結果。例如,如果你想查找年齡大于 30 的所有用戶,可以這么寫:
db.Find(&users, "age > ?", 30)
這個查詢會返回所有年齡大于 30 的用戶。
- 分頁:
Find還支持分頁查詢。你可以通過Limit和Offset方法來實現(xiàn)分頁查詢。例如,查詢前 10 條記錄:
db.Limit(10).Find(&users)
- 排序:你也可以通過
Order方法來指定查詢結果的排序方式。例如,按年齡排序:
db.Order("age desc").Find(&users)
- 返回記錄數(shù):
Find方法還會返回查詢的結果,包括查詢到的記錄數(shù)。如果沒有記錄,它會返回一個空的切片。
4.3 更新記錄
在 GORM 中,更新記錄是一個常見的操作。你可以通過 GORM 提供的幾種方法來更新記錄。以下將詳細介紹 GORM 中更新記錄的方式,包含基本更新、部分更新、批量更新等操作,并解釋每種方法的具體用法和注意事項。
基本更新:db.Save 方法
db.Save 方法用于保存(或更新)結構體中的數(shù)據(jù)。如果結構體的主鍵已經存在,GORM 會執(zhí)行 更新操作;如果主鍵不存在,GORM 會執(zhí)行 插入操作(也稱為 “upsert”)。因此,db.Save 不僅適用于更新已有記錄,也適用于插入新記錄。
示例
func main() {
var user User
db.First(&user, 1) // 查找主鍵為 1 的用戶
user.Name = "Alice Updated" // 修改字段
user.Age = 30
db.Save(&user) // 更新記錄
}
db.Save(&user)會檢查user是否已有主鍵值(假設主鍵存在)。如果存在,它將執(zhí)行更新操作,將user結構體中修改的字段更新到數(shù)據(jù)庫中。- 如果主鍵不存在,它會將
user插入到數(shù)據(jù)庫中。
注意:
Save會更新所有非零字段(即結構體中的字段如果是空值,可能不會被更新),并且會更新所有字段,即使你沒有顯式修改某個字段。- 如果你希望只更新某些字段,應該使用
Updates或Update方法。
更新部分字段:db.Updates 方法
db.Updates 方法允許你更新結構體中的 部分字段,而不是全部字段。它是一個更精確的更新方法,通常用于僅更新結構體中某些修改了的字段。
示例
func main() {
var user User
db.First(&user, 1) // 查找主鍵為 1 的用戶
db.Model(&user).Updates(User{Name: "Bob Updated", Age: 35})
}
在這個例子中:
db.Model(&user).Updates(User{Name: "Bob Updated", Age: 35})只會更新user結構體中的Name和Age字段。db.Model(&user)表明更新的是user結構體對應的數(shù)據(jù)庫記錄。Updates方法中的參數(shù)可以是一個結構體(如User{Name: "Bob Updated"}),也可以是一個map[string]interface{}(鍵是字段名,值是要更新的值)。
注意:
Updates會忽略零值字段(如空字符串、零整數(shù)等),如果某個字段的值為零,它不會被更新。db.Model(&user)用于指定要更新的模型或表。Updates會將修改過的字段進行更新,但不會更新模型中未指定的字段。
單個字段更新:db.Update 方法
如果你只需要更新某個單獨的字段,可以使用 db.Update 方法。該方法用于 更新單個字段,是 db.Updates 的簡化版本,適合只更新單一字段的場景。
示例
func main() {
var user User
db.First(&user, 1) // 查找主鍵為 1 的用戶
db.Model(&user).Update("Age", 40) // 只更新 Age 字段
}
db.Model(&user).Update("Age", 40)會將Age字段更新為40,其他字段保持不變。Update方法適用于你只需要更新單個字段的情況。
注意:
Update方法只更新指定的字段,不會影響其他字段。- 如果字段的值為零值,
Update也會更新該字段(沒有零值忽略的機制)。
批量更新:db.UpdateColumn 和 db.UpdateColumns
GORM 還提供了 UpdateColumn 和 UpdateColumns 方法,主要用于 批量更新 字段。這些方法與 Update 方法類似,但它們不會觸發(fā) GORM 的鉤子(如 BeforeSave、AfterSave 等)。
UpdateColumn 示例
db.Model(&user).UpdateColumn("Age", 45)
UpdateColumn 不會觸發(fā) GORM 的 BeforeSave 和 AfterSave 鉤子,因此適用于需要繞過這些鉤子的情況。
UpdateColumns 示例
db.Model(&user).UpdateColumns(map[string]interface{}{"Age": 50, "Name": "Charlie Updated"})
UpdateColumns 會根據(jù)傳入的字段進行批量更新。與 Update 不同,它不會觸發(fā)模型的鉤子。
注意:
- 這兩個方法直接更新字段,不會對字段的零值進行忽略。
- 它們只進行 單字段的原子更新,不會涉及到多表關聯(lián)等操作。
條件更新:db.Where 和 db.Updates
你可以在更新時通過 Where 方法指定更新的條件。Where 方法可以與 Updates 或 Update 一起使用,以便進行條件更新。
示例
db.Model(&User{}).Where("age > ?", 30).Updates(User{Name: "Updated Name"})
- 這個示例中,
Where("age > ?", 30)限定了更新條件,只有年齡大于 30 的用戶才會被更新。 Updates(User{Name: "Updated Name"})更新所有符合條件的用戶的Name字段。
注意:
Where可以幫助你構造復雜的更新條件,但也可以根據(jù)需要單獨使用(例如,按 ID 更新某些記錄)。
批量更新(多個記錄)
你可以使用 db.Model() 方法和 db.Updates() 方法來批量更新多個記錄。下面是一個批量更新的示例:
示例
db.Model(&User{}).Where("age > ?", 30).Updates(User{Name: "Batch Update"})
- 這個例子會更新所有
age > 30的用戶,將它們的Name字段修改為"Batch Update"。
注意:
Updates會更新所有符合條件的記錄,而不是只更新一個記錄。
使用事務更新多個記錄
如果你需要確保多個更新操作的原子性,可以將更新操作放入一個事務中。在 GORM 中,事務通過 db.Begin() 開始,db.Commit() 提交,db.Rollback() 回滾。
示例
tx := db.Begin()
// 執(zhí)行多個更新操作
tx.Model(&User{}).Where("age > ?", 30).Updates(User{Name: "Transactional Update"})
tx.Model(&User{}).Where("name = ?", "Bob").Update("Age", 40)
if err := tx.Commit().Error; err != nil {
tx.Rollback()
fmt.Println("Error:", err)
return
}
db.Begin()開始一個事務。tx.Commit()提交事務,tx.Rollback()在出錯時回滾事務,確保所有操作的原子性。
4.4 刪除記錄
刪除記錄可以使用 Delete 方法:
func deleteUser(db *gorm.DB) {
var user User
db.First(&user, 1) // 查找要刪除的用戶
// 刪除用戶
result := db.Delete(&user)
if result.Error != nil {
panic("Failed to delete user")
}
fmt.Println("User deleted:", user.ID)
}
5. 關系與關聯(lián)查詢
GORM 支持表之間的關系映射。比如,我們有 User 和 Post 之間的關系。一個用戶可以有多個帖子,可以使用 has many 關系。
5.1 定義關聯(lián)結構體
type Post struct {
ID uint
Title string
Body string
UserID uint // 外鍵
User User // 關聯(lián)的 User
}
5.2 關聯(lián)查詢
假設我們有 User 和 Post 兩個表,你可以使用 Preload 來加載關聯(lián)的 Post 數(shù)據(jù)。
func getUserWithPosts(db *gorm.DB) {
var user User
db.Preload("Posts").First(&user, 1)
fmt.Println("User:", user.Name)
fmt.Println("Posts:", user.Posts)
}
5.3 創(chuàng)建關聯(lián)記錄
當你插入一個帶有關聯(lián)的記錄時,可以使用 Create 方法來同時插入主表和從表數(shù)據(jù):
func createUserWithPosts(db *gorm.DB) {
user := User{Name: "Alice", Age: 28, Posts: []Post{
{Title: "Post 1", Body: "This is the first post"},
{Title: "Post 2", Body: "This is the second post"},
}}
db.Create(&user)
fmt.Println("User and Posts created:", user)
}
6. 事務
在 GORM 中,事務(Transaction) 是一個非常重要的概念,尤其是在需要確保多個數(shù)據(jù)庫操作要么全部成功,要么全部失敗的情況下。事務能夠保證操作的原子性、一致性、隔離性和持久性(即 ACID 特性)。如果在事務中的某個操作失敗,事務可以回滾,使得數(shù)據(jù)庫回到事務開始之前的狀態(tài)。
事務(Transaction)是一組數(shù)據(jù)庫操作,它們要么全部執(zhí)行,要么在發(fā)生錯誤時全部不執(zhí)行。事務在數(shù)據(jù)庫操作中提供了 原子性(Atomicity)、一致性(Consistency)、隔離性(Isolation) 和 持久性(Durability)(合稱為ACID特性):
- 原子性(Atomicity):事務中的所有操作要么都執(zhí)行,要么都不執(zhí)行。即事務是不可分割的整體。
- 一致性(Consistency):事務執(zhí)行前后,數(shù)據(jù)庫的狀態(tài)必須是一致的,符合數(shù)據(jù)庫的完整性約束。
- 隔離性(Isolation):一個事務的執(zhí)行不會被其他事務干擾。事務的執(zhí)行是相互隔離的。
- 持久性(Durability):一旦事務提交,其對數(shù)據(jù)庫的更改是永久性的,不會丟失。
在 GORM 中,你可以通過 db.Begin() 來啟動一個事務,使用 tx.Commit() 來提交事務,使用 tx.Rollback() 來回滾事務。以下是 GORM 中事務的常見用法。
6.1. 開始事務:db.Begin()
你可以通過 db.Begin() 啟動一個事務。這個方法會返回一個事務對象(*gorm.DB 類型),通過這個對象你可以執(zhí)行數(shù)據(jù)庫操作。
tx := db.Begin() // 開始事務
db.Begin()會創(chuàng)建一個事務。- 返回的
tx是事務對象,所有的數(shù)據(jù)庫操作都應通過tx來執(zhí)行,而不是直接使用db。
6.2 執(zhí)行事務中的操作
在事務中,你可以進行一系列的數(shù)據(jù)庫操作。所有的操作都應該通過事務對象 tx 來執(zhí)行,而不是直接通過 db 執(zhí)行。
示例
tx := db.Begin() // 開始事務
// 執(zhí)行多個數(shù)據(jù)庫操作
if err := tx.Create(&user).Error; err != nil {
tx.Rollback() // 操作失敗,回滾事務
return err
}
if err := tx.Model(&user).Update("Age", 30).Error; err != nil {
tx.Rollback() // 操作失敗,回滾事務
return err
}
tx.Create(&user)會在事務中插入一條記錄。tx.Model(&user).Update("Age", 30)會在事務中更新該記錄。
6.3 提交事務:tx.Commit()
當所有的操作都執(zhí)行成功時,可以調用 tx.Commit() 提交事務,將所有的變更永久保存到數(shù)據(jù)庫。
if err := tx.Commit().Error; err != nil {
tx.Rollback() // 提交失敗,回滾事務
return err
}
tx.Commit()會提交事務,執(zhí)行所有的操作并將它們持久化到數(shù)據(jù)庫。
6.4 回滾事務:tx.Rollback()
如果在事務過程中遇到錯誤,應該調用 tx.Rollback() 來回滾事務。這樣,所有在事務中執(zhí)行的操作都會撤銷,數(shù)據(jù)庫將恢復到事務開始前的狀態(tài)。
if err := tx.Rollback().Error; err != nil {
fmt.Println("Error during rollback:", err)
return err
}
tx.Rollback()會撤銷事務中的所有操作。
6.5 在事務中使用錯誤處理
通常,事務中的操作需要進行錯誤處理。只要有任何一項操作失敗,應該調用 tx.Rollback() 進行回滾。
tx := db.Begin()
// 執(zhí)行操作 1
if err := tx.Create(&user).Error; err != nil {
tx.Rollback() // 錯誤發(fā)生,回滾事務
return err
}
// 執(zhí)行操作 2
if err := tx.Model(&user).Update("Age", 30).Error; err != nil {
tx.Rollback() // 錯誤發(fā)生,回滾事務
return err
}
// 所有操作成功,提交事務
if err := tx.Commit().Error; err != nil {
tx.Rollback() // 提交失敗,回滾事務
return err
}
6.6 事務中的多表操作
在事務中,你可以操作多個表,只要使用同一個事務對象 tx,所有的表操作都將在一個事務內完成。
示例:多表操作
tx := db.Begin()
// 插入用戶表
if err := tx.Create(&user).Error; err != nil {
tx.Rollback()
return err
}
// 更新訂單表
if err := tx.Model(&order).Update("Status", "Shipped").Error; err != nil {
tx.Rollback()
return err
}
// 提交事務
if err := tx.Commit().Error; err != nil {
tx.Rollback()
return err
}
- 這里插入了一個用戶并更新了訂單狀態(tài),所有操作都在同一個事務中進行。
6.7 事務的嵌套
GORM 不直接支持嵌套事務(即在一個事務中開啟另一個事務)。但是,你可以通過手動管理事務嵌套。在嵌套事務中,只有最外層的事務會決定是否提交或回滾。
tx := db.Begin()
// 外部事務操作
if err := tx.Create(&user).Error; err != nil {
tx.Rollback()
return err
}
nestedTx := tx.Begin() // 開始嵌套事務
// 嵌套事務操作
if err := nestedTx.Model(&order).Update("Status", "Shipped").Error; err != nil {
nestedTx.Rollback() // 嵌套事務回滾
tx.Rollback() // 外部事務回滾
return err
}
nestedTx.Commit() // 嵌套事務提交
tx.Commit() // 外部事務提交
- 上述代碼演示了如何在一個事務中手動開啟一個嵌套事務。嵌套事務的提交和回滾會影響最外層事務。
6.8 事務中的并發(fā)問題
在事務中使用并發(fā)操作時,必須小心并發(fā)引起的 數(shù)據(jù)競爭 和 死鎖 問題。GORM 默認使用 隔離級別 為 ReadCommitted,你可以通過配置數(shù)據(jù)庫的事務隔離級別來避免一些并發(fā)問題。
tx := db.Begin().Set("gorm:query_option", "LOCK IN SHARE MODE")
// 事務操作
此時,LOCK IN SHARE MODE 會在查詢時加鎖,避免其他事務修改同一行數(shù)據(jù),防止數(shù)據(jù)不一致。
總結
GORM 是一個功能強大且易于使用的 Go 語言 ORM 庫,能夠讓開發(fā)者以面向對象的方式與數(shù)據(jù)庫交互,減少了 SQL 語句的編寫和管理的復雜度。它適合需要處理數(shù)據(jù)庫的 Go 項目,特別是那些涉及大量數(shù)據(jù)操作、需要事務支持和多表關聯(lián)的應用。
到此這篇關于GO中GORM 使用教程的文章就介紹到這了,更多相關GO GORM 使用內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Go實現(xiàn)自動解壓縮包以及讀取docx/doc文件內容詳解
在開發(fā)過程中,我們常常需要處理壓縮包和文檔文件。本文將介紹如何使用Go語言自動解壓縮包和讀取docx/doc文件,需要的可以參考一下2023-03-03
Golang使用singleflight解決并發(fā)重復請求
高并發(fā)的場景下,經常會出現(xiàn)并發(fā)重復請求資源的情況,singleflight是golang內置的一個包,這個包提供了對重復函數(shù)調用的抑制功能,所以下面我們就來看看如何使用它解決并發(fā)重復請求吧2023-08-08
Ubuntu18.04 LTS搭建GO語言開發(fā)環(huán)境過程解析
這篇文章主要介紹了Ubuntu18.04 LTS搭建GO語言開發(fā)環(huán)境過程解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2020-11-11

