GORM 是 Go 語言中功能強大的 ORM(對象關系映射)框架,支持 MySQL、PostgreSQL、SQLite、SQL Server 等主流數據庫。以下是 GORM 的核心概念和用法詳解:
??一、基礎入門??
1. 安裝
go get -u gorm.io/gorm
go get -u gorm.io/driver/mysql # 按需選擇數據庫驅動
所以我們看到安裝的最新版本是 1.26.0.這個不是Gorm1.0的意思。是GORM2.0.參考官方2.0的文檔。https://gorm.io/zh_CN/docs/
2. 連接數據庫
import ("gorm.io/driver/mysql""gorm.io/gorm"
)dsn := "user:password@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
Gorm 有一個?默認 logger 實現,默認情況下,它會打印慢 SQL 和錯誤
Logger 接受的選項不多,您可以在初始化時自定義它,例如:
newLogger := logger.New(log.New(os.Stdout, "\r\n", log.LstdFlags), // io writerlogger.Config{SlowThreshold: time.Second, // Slow SQL thresholdLogLevel: logger.Silent, // Log levelIgnoreRecordNotFoundError: true, // Ignore ErrRecordNotFound error for loggerParameterizedQueries: true, // Don't include params in the SQL logColorful: false, // Disable color}, )// Globally mode db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{Logger: newLogger, })// Continuous session mode tx := db.Session(&Session{Logger: newLogger}) tx.First(&user) tx.Model(&user).Update("Age", 18) |
日志級別
GORM 定義了這些日志級別:Silent
、Error
、Warn
、Info
??二、模型定義??
1. 結構體與表映射
type User struct {gorm.Model // 內置字段:ID, CreatedAt, UpdatedAt, DeletedAtName string `gorm:"size:255"`Age intEmail string `gorm:"uniqueIndex size:255"` // 唯一索引
}
2. 自定義表名
func (User) TableName() string {return "custom_users" // 自定義表名
}
3.自動遷移
GORM 提供的自動遷移功能是指它能夠根據定義的 Go 結構體(模型)自動創建、更新或刪除數據庫中的表結構,以此保持模型和數據庫表結構的一致性。這一功能極大地簡化了數據庫表結構管理的流程。例如,當你新增一個字段到結構體中,自動遷移會嘗試在數據庫表中添加該字段;若刪除了結構體中的某個字段,自動遷移也可能根據配置刪除表中的相應列。
工作原理
- 解析模型定義:GORM 會解析你定義的 Go 結構體,識別其中的字段、類型、標簽等信息。例如,對于以下?
User
?結構
type User struct {gorm.Model // 內置字段:ID, CreatedAt, UpdatedAt, DeletedAtName string `gorm:"size:255"`Age intEmail string `gorm:"uniqueIndex size:255"` // 唯一索引
}func (User) TableName() string {return "custom_users" // 自定義表名
}
GORM 會分析出?User
?表應該包含?id
、created_at
、updated_at
、deleted_at
(來自?gorm.Model
)、name
、email
?和?age
?這些字段,并且?email
?字段需要有唯一約束。
2.?生成 SQL 語句:根據解析結果,GORM 生成相應的 SQL 語句。如果數據庫中不存在?custom_users
表,會生成?CREATE TABLE
?語句來創建表;若表已存在,會生成?ALTER TABLE
?語句來更新表結構。
3.?執行 SQL 語句:GORM 將生成的 SQL 語句發送到數據庫執行,從而完成表結構的創建或更新。
使用示例
package mainimport ("gorm.io/driver/mysql""gorm.io/gorm"
)// User 模型定義
type User struct {gorm.Model // 內置字段:ID, CreatedAt, UpdatedAt, DeletedAtName string `gorm:"size:255"`Age intEmail string `gorm:"uniqueIndex size:255"` // 唯一索引
}func (User) TableName() string {return "custom_users" // 自定義表名
}func main() {dsn := "user:password@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})if err != nil {panic("failed to connect database")}// 自動遷移 User 模型db.AutoMigrate(&User{})
}
輸出結果:
在上述代碼中,調用?db.AutoMigrate(&User{})
?會根據?User
?結構體的定義在數據庫中創建或更新?User
?表。
注意事項
- 數據丟失風險:自動遷移可能會導致數據丟失。例如,當你刪除結構體中的某個字段時,自動遷移可能會刪除數據庫表中的相應列,該列的數據就會丟失。所以在生產環境中最好不要使用該功能。
??三、CRUD 操作??
1. 創建記錄
// 創建單條
user := User{Name: "Alice", Age: 25}
db.Create(&user)// 批量插入
users := []User{{Name: "Bob"}, {Name: "Charlie"}}
db.CreateInBatches(users, 100) // 每批100條
2. 查詢數據
var users []User// 查詢全部
db.Find(&users)// 條件查詢
db.Where("age > ?", 18).Find(&users)
db.First(&user, 1) // 查找ID=1的第一條記錄
db.Last(&user) // 最后一條記錄// 高級查詢
db.Select("name, age").Where("age > ?", 20).Order("age desc").Limit(10).Find(&users)
3. 更新數據
// 更新單個字段
db.Model(&user).Update("Age", 30)// 批量更新
db.Model(&User{}).Where("age < ?", 30).Update("Age", gorm.Expr("Age + ?", 1))
4. 刪除數據
// 軟刪除(默認使用 DeletedAt 字段)
db.Delete(&user)// 硬刪除(物理刪除)
db.Unscoped().Delete(&user)
??四、關聯關系??
一對一關聯
package mainimport "gorm.io/gorm"// User 用戶模型
type User struct {gorm.ModelName stringProfile Profile
}// Profile 個人資料模型
type Profile struct {gorm.ModelUserID uintAddress string
}
在這個例子中,User
?結構體包含一個?Profile
?字段,Profile
?結構體包含一個?UserID
?字段作為外鍵,關聯到?User
?表。
遷移和操作示例
package mainimport ("fmt""gorm.io/driver/mysql""gorm.io/gorm"
)func ConnectDB() (*gorm.DB, error) {dsn := "user:password@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})if err != nil {return nil, err}return db, nil
}func main() {db, err := ConnectDB()if err != nil {panic("failed to connect database")}// 自動遷移模型db.AutoMigrate(&User{}, &Profile{})// 創建用戶和關聯的個人資料user := User{Name: "John Doe",Profile: Profile{Address: "123 Main St",},}db.Create(&user)// 查詢用戶及其個人資料var retrievedUser Userdb.Preload("Profile").First(&retrievedUser, user.ID)fmt.Printf("User: %s, Address: %s\n", retrievedUser.Name, retrievedUser.Profile.Address)
}
在這個例子中,我們使用?Preload
?方法預加載用戶的個人資料。
一對多關聯
一對多關聯表示一個模型的一條記錄可以關聯到另一個模型的多條記錄。例如,一個用戶可以有多個帖子。
package mainimport "gorm.io/gorm"// User 用戶模型
type User struct {gorm.ModelName stringPosts []Post
}// Post 帖子模型
type Post struct {gorm.ModelTitle stringUserID uint
}
在這個例子中,User
?結構體包含一個?Posts
?切片,Post
?結構體包含一個?UserID
?字段作為外鍵,關聯到?User
?表。
遷移和操作示例
package mainimport ("fmt""gorm.io/driver/mysql""gorm.io/gorm"
)func ConnectDB() (*gorm.DB, error) {dsn := "user:password@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})if err != nil {return nil, err}return db, nil
}func main() {db, err := ConnectDB()if err != nil {panic("failed to connect database")}// 自動遷移模型db.AutoMigrate(&User{}, &Post{})// 創建用戶和關聯的帖子user := User{Name: "John Doe",Posts: []Post{{Title: "First Post"},{Title: "Second Post"},},}db.Create(&user)// 查詢用戶及其帖子var retrievedUser Userdb.Preload("Posts").First(&retrievedUser, user.ID)fmt.Printf("User: %s, Posts count: %d\n", retrievedUser.Name, len(retrievedUser.Posts))
}
在這個例子中,我們同樣使用?Preload
?方法預加載用戶的所有帖子。
多對多關聯
多對多關聯表示一個模型的一條記錄可以關聯到另一個模型的多條記錄,反之亦然。例如,一個用戶可以有多個角色,一個角色可以被多個用戶擁有。
定義模型
package mainimport "gorm.io/gorm"// User 用戶模型
type User struct {gorm.ModelName stringRoles []Role `gorm:"many2many:user_roles;"`
}// Role 角色模型
type Role struct {gorm.ModelName stringUsers []User `gorm:"many2many:user_roles;"`
}
在這個例子中,User
?結構體和?Role
?結構體都包含一個切片,通過?many2many
?標簽指定關聯表為?user_roles
。
遷移和操作示例
package mainimport ("fmt""gorm.io/driver/mysql""gorm.io/gorm"
)func ConnectDB() (*gorm.DB, error) {dsn := "user:password@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})if err != nil {return nil, err}return db, nil
}func main() {db, err := ConnectDB()if err != nil {panic("failed to connect database")}// 自動遷移模型db.AutoMigrate(&User{}, &Role{})// 創建用戶和角色user := User{Name: "John Doe"}role := Role{Name: "Admin"}db.Create(&user)db.Create(&role)// 關聯用戶和角色db.Model(&user).Association("Roles").Append(&role)// 查詢用戶及其角色var retrievedUser Userdb.Preload("Roles").First(&retrievedUser, user.ID)fmt.Printf("User: %s, Roles count: %d\n", retrievedUser.Name, len(retrievedUser.Roles))
}
我們使用?Association
?方法將用戶和角色關聯起來,然后使用?Preload
?方法預加載用戶的所有角色。
- 預加載:使用?
Preload
?方法可以在查詢主模型時同時加載關聯的模型數據,避免 N+1 查詢問題。 - 關聯操作:對于多對多關聯,可以使用?
Association
?方法進行關聯的添加、刪除等操作。 - 遷移:在使用關聯關系之前,需要確保模型已經通過?
AutoMigrate
?方法進行了遷移,以創建相應的表和外鍵約束。
???
- ??循環引用??:確保模型間沒有相互嵌套導致無限遞歸。
- ??性能優化??:避免?
SELECT *
,明確指定需要的字段。 - ??索引優化??:在頻繁查詢的字段上添加索引(如?
gorm:"index"
)。