目錄
🏆前言
1.Gorm的簡介
2.GORM連接數據庫?
2.1 配置DSN?
Mysql,TiDB,MariaDB
PostgreSQL
SQL Server
?SQLite
2.2 gorm.Open連接數據庫
3.數據庫連接池的配置??
4.使用GORM對數據庫進行操作(重點)?
4.1 創建數據表
4.2 CRUD操作?
GORM利用原生SQL執行
🏆前言
? ? ? ? 通過上一篇Go語言學習(16)Gin后端框架的學習,了解了基本的Go語言如何使用Gin進行前后端數據的傳輸,本文將進一步學習后端,了解如何使用Gorm與Mysql數據庫進行數據交互,實現基本的增刪改查功能。
1.Gorm的簡介
????????GORM 是 Go 語言中最流行的 ORM(對象關系映射)庫之一,支持主流數據庫(MySQL、PostgreSQL、SQLite、SQL Server 等),提供簡潔的 API 和強大的功能。其具有以下基本特點:
- 簡潔易用:通過定義結構體來映射數據庫表,簡化數據操作。
- 功能全面:支持CRUD、事務、預加載、關聯關系、自動遷移等常見功能。
- 擴展性強:內置鉤子函數、插件機制以及對多種數據庫(MySQL、PostgreSQL、SQLite、SQL Server等)的支持。
- 性能優秀:經過大量優化,能夠在高并發場景下保持穩定性能。?
? ? ? ? 當然Go語言當中的ORM庫不止Gorm一個,以下是一些其它Go當中的ORM庫的比較,可根據自身需求,進行選擇學習:
技術棧 | 性能 | 易用性 | 功能 | 適用場景 |
---|---|---|---|---|
Gorm | 優秀,適合高并發場景 | 非常友好,API簡潔明了,文檔齊全 | 功能全面,支持多種數據庫,擴展性強 | 適合各種規模的項目,尤其是需要高性能和復雜數據庫操作的場景 |
Xorm | 性能優異,經過大量優化 | API簡潔,支持鏈式調用 | 支持基本的CRUD操作,查詢構建器靈活 | 適合中小型項目,特別是對性能要求較高的場景 |
Beego ORM | 性能中等 | 與Beego框架無縫集成,使用方便 | 功能全面,但非Beego項目中配置復雜 | 適合使用Beego框架的Web開發項目 |
Go-PG | 針對PostgreSQL優化,性能良好 | API設計符合PostgreSQL習慣,但學習成本較高 | 充分利用PostgreSQL特性,支持高級查詢和事務管理 | 適用于PostgreSQL數據庫的項目 |
upper.io/db | 性能中等 | 模塊化設計,易于擴展和定制,但配置復雜 | 支持多種數據庫,模塊化設計 | 適用于需要支持多種數據庫的復雜項目 |
—🍅?GORM的官方文檔連接👉:GORM官方文檔?
2.GORM連接數據庫?
? ? ? ?首先我們需要使用以下命令,安裝GORM與Mysql驅動引入到本地項目當中:
go get -u gorm.io/gorm
go get -u gorm.io/driver/mysql
????????使用GORM連接數據庫,大體可以分為兩個步驟:
- 🌸配置DSN(Data Source Name,數據源名稱)
- 🌸使用gorm.Open連接數據庫
2.1 配置DSN?
?????????DSN(Data Source Name) 是一種用于連接數據庫的字符串,它包含了連接到數據庫所需的所有信息,如數據庫類型、主機地址、端口號、用戶名、密碼、數據庫名稱等。DSN通常用于數據庫驅動程序中,以簡化數據庫連接的配置。不同的數據庫連接中DSN的配置也不相同。
-
Mysql,TiDB,MariaDB
dsn := "user:password@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"//填上參數后的例子
//username = root
//password = 123456
//host = localhost
//port = 3306
//Dbname = gorm
root:123456@tcp(localhost:3306)/gorm?charset=utf8&parseTime=True&loc=Local
?🌹各參數含義:
- user:數據庫用戶名。
- password:數據庫密碼。
- 127.0.0.1:數據庫服務器的IP地址。
- 3306:數據庫服務監聽的端口號。
- dbname:要連接的數據庫名稱。
- charset=utf8mb4:字符集設置。
- parseTime=True:啟用時間解析。
- loc=Local:時區設置。
? ? ? ? 由于TiDB,MariaDB與MySQL在功能上幾乎完全兼容,因此可以按照MySQL的DSN格式來連接:,但需要注意TiDB的默認端口號為4000,而不是3306。
-
PostgreSQL
dsn := "host=localhost user=gorm dbname=gorm password=gorm port=5432 sslmode=disable"
🌹各參數含義:
- host:數據庫服務器的主機名或IP地址。
- user:數據庫用戶名。
- dbname:要連接的數據庫名稱。
- password:數據庫密碼。
- port:數據庫服務監聽的端口號。
- sslmode:SSL模式,如disable、require等?
-
SQL Server
dsn := "sqlserver://username:password@localhost:1433?database=your_db"
?🌹各參數含義:
- username:數據庫用戶名。
- password:數據庫密碼。
- localhost:數據庫服務器的主機名或IP地址。
- 1433:數據庫服務監聽的端
- your_db:要連接的數據庫名稱。
-
?SQLite
dsn := "gorm.db"
SQLite的DSN通常是一個文件路徑,表示數據庫文件的位置。
2.2 gorm.Open連接數據庫
? ? ? ? 配置好DSN之后,便可以使用open函數連接數據庫,下面以Mysq連接為例子。
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})if err != nil {panic("failed to connect database")}
? ? ? ? ?使用mysql.open讀取dsn進行數據庫連接,&gorm.Config{}?用于配置 GORM 的全局行為(可選參數),例如:Logger: 自定義日志輸出;SkipDefaultTransaction: 禁用默認事務;NamingStrategy: 表名和列名命名策略,此處使用空結構體 {} 表示使用默認配置。返回的db是一個 *gorm.DB 對象,代表數據庫連接池,后續所有數據庫操作都基于此對象。其余數據庫連接也是同理。
此外GORM 允許通過 DriverName 選項自定義 MySQL 驅動,例如:?
import (_ "example.com/my_mysql_driver""gorm.io/driver/mysql""gorm.io/gorm"
)db, err := gorm.Open(mysql.New(mysql.Config{DriverName: "my_mysql_driver",DSN: "gorm:gorm@tcp(localhost:9910)/gorm?charset=utf8&parseTime=True&loc=Local", // data source name, 詳情參考:https://github.com/go-sql-driver/mysql#dsn-data-source-name
}), &gorm.Config{})
3.數據庫連接池的配置??
????????數據庫連接池就像餐廳里的一群“備用服務員”。這些服務員已經站在廚房門口,隨時準備接單。當顧客來點菜時,直接從這群備用服務員中挑一個,用完后服務員會回到原位,繼續等待下一位顧客。這樣就避免了每次都要重新找服務員(建立連接)的麻煩,效率更高。然而連接池的設置并不能過大或者或小。
連接池設置過大的影響:
- 資源浪費:設置過大的連接池可能導致過多的數據庫連接被創建,占用過多的系統資源(如內存和CPU),從而導致資源浪費。
- 性能下降:過多的連接可能導致數據庫服務器負載過高,影響整體應用程序性能,甚至可能導致系統崩潰。
- 資源碎片化:過多的連接可能導致系統資源碎片化,降低系統的穩定性和響應速度。
連接池設置過小的影響:
- 連接不足:設置過小的連接池可能導致在高并發情況下請求無法及時獲取數據庫連接,從而影響并發性能和響應速度。
- 頻繁創建和銷毀連接:連接池過小會導致頻繁地創建和銷毀連接,增加了系統的開銷。
- 請求等待:在高并發情況下,請求可能會等待空閑連接,導致延遲增加,用戶體驗下降
在GORM中,我們可以參照以下示例,對連接池進行有關配置。?
dsn := "root:123456@tcp(127.0.0.1:3306)/golang?charset=utf8mb4&parseTime=True&loc=Local"
db, _ := gorm.Open(mysql.Open(dsn), &gorm.Config{})sqlDB, err := db.DB()
if err != nil {panic("failed to get database connection")
}// 設置最大打開的連接數
sqlDB.SetMaxOpenConns(10)
// 設置最大空閑連接數
sqlDB.SetMaxIdleConns(5)
// 設置連接的最大存活時間
sqlDB.SetConnMaxLifetime(time.Hour)
下面解釋一下上述代碼最后三行代碼的配置分別代表什么,有什么作用。
// 設置最大打開的連接數
sqlDB.SetMaxOpenConns(10)
🌈通俗解釋:
想象你開了一家餐廳,最多只能同時雇傭10個服務員。無論有多少顧客來,你最多只能同時派10個服務員去服務。這個數字就是“最大打開的連接數”。??技術解釋:
最大打開的連接數是指數據庫連接池中同時可以打開的數據庫連接的最大數量。如果設置為10,那么最多只能有10個連接同時處于“打開”狀態,用于執行數據庫操作。如果所有連接都被占用,新的請求需要等待,直到有連接被釋放。
// 設置最大空閑連接數
sqlDB.SetMaxIdleConns(5)
🌈通俗解釋:
繼續用餐廳的例子,假設你有10個服務員,但并不是所有服務員都一直有活干。有些服務員可能暫時沒事做(空閑)。你希望最多有5個服務員可以“待命”,隨時準備接單。這個數字就是“最大空閑連接數”。
??技術解釋:
最大空閑連接數是指連接池中允許存在的空閑連接的最大數量。空閑連接是指當前沒有被使用的連接。如果空閑連接數超過這個值,多余的連接會被關閉,以節省資源。
// 設置連接的最大存活時間
sqlDB.SetConnMaxLifetime(time.Hour)
🌈通俗解釋:
假設你規定每個服務員最多只能連續工作1小時,之后必須休息或換人。這個時間限制就是“連接最大存活時間”。??技術解釋:
連接最大存活時間是指一個數據庫連接在連接池中可以存在的最長時間。如果一個連接已經存在了超過這個時間,它會被自動關閉,并從連接池中移除。這樣可以確保連接不會因為長時間使用而出現性能問題或資源泄漏。
4.使用GORM對數據庫進行操作(重點)?
? ? ? ? 首先,如果對結構體以及標簽(Tag)不熟悉的uu,還請移步到結構體標簽與反射機制,GORM對數據庫的基本操作與結構體類型數據非常相關。對結構體不熟悉的話,可能下面的代碼會看不懂QAQ。
4.1 創建數據表
? ? ? ? 假設我們現在需要構建一個 Students 表,那么我們得先創建一個Students的結構體類型數據,這一步我們稱為 定義操作模型 。如下:
type Students struct {StudentID uint `gorm:"primaryKey;autoIncrement"`Name string `gorm:"size:50;not null"`Major string `gorm:"size:100"`Gender string `gorm:"type:enum('男', '女');not null"`Phone string `gorm:"size:11;not null"`
}
? ? ? ? 這里結構體使用 gorm 標簽,以下是關于 gorm 標簽常用的標簽字段表:
標簽 | 說明 | 示例 |
---|---|---|
primaryKey | 標記字段為主鍵 | gorm:"primaryKey" |
autoIncrement | 主鍵自增(僅支持整數類型) | gorm:"primaryKey;autoIncrement" |
column | 自定義數據庫列名 | gorm:"column:user_name" |
type | 指定數據庫字段類型 | gorm:"type:varchar(100)" |
size | 設置字段長度(如字符串類型) | gorm:"size:255" |
default | 設置字段默認值 | gorm:"default:'anonymous'" |
not null | 字段不可為空 | gorm:"not null" |
unique | 唯一約束(值不可重復) | gorm:"unique" |
uniqueIndex | 創建唯一索引(可指定索引名) | gorm:"uniqueIndex:idx_email" |
index | 創建普通索引 | gorm:"index" |
check | 自定義檢查約束 | gorm:"check:age > 18" |
autoCreateTime | 記錄創建時間(time.Time ?類型) | gorm:"autoCreateTime" |
autoUpdateTime | 記錄更新時間(time.Time ?類型) | gorm:"autoUpdateTime" |
foreignKey | 定義外鍵字段 | gorm:"foreignKey:UserID" |
references | 定義關聯的主鍵字段 | gorm:"references:ID" |
many2many | 定義多對多關聯的中間表 | gorm:"many2many:user_groups;" |
polymorphic | 多態關聯(用于多模型共享關聯) | gorm:"polymorphic:Owner" |
?????????接著連接數據庫之后,使用?db.AutoMigrate() 啟用自動遷移模式,此時如果Students表不存在,則會依據上述定義的結構體,自動創建一個Students表,如果表已存在但是表的結構與結構體的結構不一樣,則會自動更新數據表的結構與結構體保持一致。?
// 連接到Mysql數據庫dsn := "root:123456@tcp(127.0.0.1:3306)/golang?charset=utf8mb4&parseTime=True&loc=Local"db, _ := gorm.Open(mysql.Open(dsn), &gorm.Config{})//自動遷移模式(如果表不存在,會自動創建)db.AutoMigrate(&Students{})
我們可以使用 Navicat 或者 sql語句 直接查看創建的數據表:
這里補充一下,GORM的結構體字段到數據庫字段的轉化是采取?Snake Case?風格轉換:
- 結構體的名稱必須首字母大寫 ,并和數據庫表名稱對應。
- 結構體中的字段名稱首字母必須大寫,并和數據庫表中的字段一一對應
- Snake Case命名風格,就是各個單詞之間用下劃線(_)分隔,例如: CreateTime的Snake Case風格命名為create_time。
在創建數據表過程中,默認情況表名是結構體名稱的復數形式。如果我們的結構體名稱定義成 User,表示這個模型默認操作的是 users 表。
4.2 CRUD操作?
下面以User表作為示例:
type User struct {ID uint `gorm:"primaryKey"` // 主鍵Name string `gorm:"size:100"` // 字符串長度限制Age intEmail string `gorm:"unique"` // 唯一約束
}
1.新增數據:
// 單條插入
user := User{Name: "Alice", Age: 25, Email: "alice@example.com"}
result := db.Create(&user) // 插入數據
if result.Error != nil {panic("插入失敗: " + result.Error.Error())
}
fmt.Println("插入成功,ID:", user.ID) // 自動填充主鍵// 批量插入
users := []User{{Name: "Bob", Age: 30, Email: "bob@example.com"},{Name: "Charlie", Age: 28, Email: "charlie@example.com"},
}
db.Create(&users)
2.查詢數據
// 查詢單條(按主鍵)
var user User
db.First(&user, 1) // 查詢 ID=1 的記錄
fmt.Println(user.Name)// 條件查詢
db.Where("age > ?", 20).First(&user) // 第一條符合條件的記錄
db.Where("name LIKE ?", "%Ali%").Find(&user)// 查詢多條
var users []User
db.Where("age BETWEEN ? AND ?", 20, 30).Find(&users)// 選擇特定字段
db.Select("name", "age").Find(&users)// 排序和分頁
db.Order("age desc").Limit(10).Offset(0).Find(&users)
3.更新數據
// 更新單個字段
db.Model(&user).Update("age", 26) // 將 user 的 age 更新為 26// 更新多個字段
db.Model(&user).Updates(User{Name: "Alice Smith", Age: 27})// 條件更新(更新所有符合條件的記錄)
db.Where("age < ?", 18).Updates(User{Name: "未成年"})// 注意:Updates 默認忽略零值(如 Age:0),需用 Select 強制更新
db.Model(&user).Select("Age").Updates(User{Age: 0})
4.刪除數據
// 刪除單條記錄(軟刪除,需模型包含 DeletedAt 字段)
db.Delete(&user) // 實際執行 UPDATE 設置 deleted_at 時間// 硬刪除(物理刪除)
db.Unscoped().Delete(&user) // 直接從數據庫刪除// 條件刪除
db.Where("email LIKE ?", "%example.com").Delete(&User{})
- 軟刪除:并不會真正從數據庫中移除數據,而是通過添加一個標記字段(如is_deleted或deleted_at)來表示數據是否被“刪除”。查詢時,通常會過濾掉這些被標記為已刪除的數據。
- 硬刪除:硬刪除會直接從數據庫中移除數據,這些數據一旦刪除,就無法恢復(除非有備份)
GORM利用原生SQL執行
????????以上的CRUD操作都是基于GORM的封裝函數實現,如果習慣了寫 sql 的uu,也可以使用 db.Exec()以及db.Raw()來執行sql語句,具體使用可參考下面的示例代碼
完整實例:
package mainimport ("fmt""gorm.io/driver/mysql""gorm.io/gorm"
)type User struct {ID uintName stringAge intEmail string
}func main() {dsn := "root:123456@tcp(127.0.0.1:3306)/test_db?charset=utf8mb4&parseTime=True&loc=Local"db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})if err != nil {panic("數據庫連接失敗")}// 插入db.Exec("INSERT INTO users (name, age, email) VALUES (?, ?, ?)", "李四", 28, "lisi@example.com")// 查詢var user Userdb.Raw("SELECT * FROM users WHERE name = ?", "李四").Scan(&user)fmt.Printf("查詢結果: %+v\n", user)// 更新db.Exec("UPDATE users SET age = ? WHERE name = ?", 30, "李四")// 刪除db.Exec("DELETE FROM users WHERE name = ?", "李四")
}
? ? ? ? 至此17篇,我們終于學完了Go的后端開發的最為基礎的內容。