go gorm简单使用方法
GORM 是 Go 语言中一个非常流行的 ORM(对象关系映射)库,它允许开发者通过结构体来定义数据库表结构,并提供了丰富的 API 来操作数据库。
安装
go get -u gorm.io/gorm
go get -u gorm.io/driver/sqlite
表结构
在 gorm
中定义表结构通常是通过定义一个结构体(struct)来完成的,其中每个字段对应数据库表中的列。
以下是一个使用 GORM 定义的简单用户表结构的例子:
package mainimport ("gorm.io/gorm""gorm.io/dialects/mysql"
)// User 定义了用户的表结构
type User struct {// gorm.Model 包含了一些默认字段:ID, CreatedAt, UpdatedAt, DeletedAtgorm.Model// 用户名,设置为唯一且不能为空Name string `gorm:"column:user_name;type:varchar(50);unique;not null"`// 手机号,设置为唯一且不能为空Phone string `gorm:"type:varchar(20);unique;not null"`// 密码,存储为二进制大对象Password []byte `gorm:"type:blob" json:"-"`
}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")}// 自动迁移模式 - 根据结构体创建或更新表结构db.AutoMigrate(&User{})
}
在这个例子中:
gorm.Model
是一个便捷的结构体,包含了ID
,CreatedAt
,UpdatedAt
,DeletedAt
字段。Name
和Phone
字段设置了数据库列名、类型、是否唯一以及是否可以为空。Password
字段被设置为blob
类型,并且不包含在 JSON 序列化中。dsn
(Data Source Name) 是用来连接 MySQL 数据库的字符串。db.AutoMigrate(&User{})
会自动根据User
结构体创建或更新对应的数据库表结构。
在 GORM 中,单表操作包括创建、查询、更新和删除记录。以下是一些基本的单表操作示例:
单表操作
创建记录
创建记录通常使用 Create
方法。
// 创建一个新的 User 实例
user := User{Name: "张三", Age: 30, Email: "zhangsan@example.com"}
// 使用 Create 方法创建记录
result := db.Create(&user)
// 检查错误
if result.Error != nil {panic(result.Error)
}
// 输出插入后的用户 ID
fmt.Printf("插入后的用户 ID: %d\n", user.ID)
查询记录
查询记录可以使用 First
, Find
, Take
, Last
等方法。
查询第一个匹配的记录
// 查询第一个用户
var user User
result := db.First(&user)
if result.Error != nil {panic(result.Error)
}
fmt.Printf("查询到的用户: %+v\n", user)
查询所有记录
// 查询所有用户
var users []User
result := db.Find(&users)
if result.Error != nil {panic(result.Error)
}
fmt.Printf("查询到的用户列表: %+v\n", users)
条件查询
// 查询年龄为 30 的用户
var user User
result := db.Where("age = ?", 30).First(&user)
if result.Error != nil {panic(result.Error)
}
fmt.Printf("查询到的用户: %+v\n", user)
更新记录
更新记录通常使用 Update
或 Updates
方法。
更新单个字段
// 更新第一个用户的年龄
result := db.Model(&User{}).Where("id = ?", 1).Update("age", 31)
if result.Error != nil {panic(result.Error)
}
fmt.Printf("更新影响的行数: %d\n", result.RowsAffected)
更新多个字段
// 更新第一个用户的年龄和邮箱
result := db.Model(&User{}).Where("id = ?", 1).Updates(User{Age: 32, Email: "zhangsan_new@example.com"})
if result.Error != nil {panic(result.Error)
}
fmt.Printf("更新影响的行数: %d\n", result.RowsAffected)
删除记录
删除记录使用 Delete
方法。
// 删除 ID 为 1 的用户
result := db.Delete(&User{}, 1)
if result.Error != nil {panic(result.Error)
}
fmt.Printf("删除影响的行数: %d\n", result.RowsAffected)
软删除
如果你的模型包含了 gorm.DeletedAt
字段(该字段也被包含在gorm.Model
中),那么该模型将会自动获得软删除的能力
当调用Delete
时,GORM并不会从数据库中删除该记录,而是将该记录的DeleteAt
设置为当前时间,而后的一般查询方法将无法查找到此条记录。
查找被删除的记录
DB.Unscoped().Find(&users)
永久删除
db.Unscoped().Delete(&user)
单标高级查询
条件查询
Where 子句
// 查询年龄大于 30 的用户
var users []User
db.Where("age > ?", 30).Find(&users)
Struct & Map 条件
// 使用结构体作为条件
db.Where(&User{Name: "张三", Age: 30}).Find(&users)
// 使用 Map 作为条件
db.Where(map[string]interface{}{"name": "张三", "age": 30}).Find(&users)
Not 条件
// 查询年龄不等于 30 的用户
db.Not("age = ?", 30).Find(&users)
Or 条件
// 查询年龄大于 30 或者名字是李四的用户
db.Where("age > ?", 30).Or("name = ?", "李四").Find(&users)
内联条件
// 内联条件查询
db.Find(&users, "age > ?", 30)
Select 子句
// 仅查询特定字段
db.Select("name", "age").Find(&users)
Order By
// 根据年龄排序
db.Order("age desc").Find(&users)
Limit & Offset
// 限制返回结果的数量,并设置偏移量
db.Limit(10).Offset(20).Find(&users)
Count
// 计算符合条件的记录数量
var count int64
db.Where("age > ?", 20).Model(&User{}).Count(&count)
Group By & Having
// 分组并使用 Having 子句
db.Model(&User{}).Select("name, sum(age) as total_age").Group("name").Having("total_age > ?", 100).Find(&users)
Joins
// 内连接查询
db.Joins("JOIN orders ON orders.user_id = users.id").Where("orders.status = ?", "paid").Find(&users)
Scopes
// 定义一个 Scope
func AgeGreaterThan(age int) func(db *gorm.DB) *gorm.DB {return func(db *gorm.DB) *gorm.DB {return db.Where("age > ?", age)}
}
// 使用 Scope 查询
db.Scopes(AgeGreaterThan(30)).Find(&users)
Pluck
// 提取单个列的值
var ages []int
db.Model(&User{}).Pluck("age", &ages)
Scan
// 将查询结果映射到不同的结构体
type Result struct {Name stringAge int
}
var result Result
db.Model(&User{}).Select("name, age").Where("id = ?", 1).Scan(&result)
多表关系与操作
在 GORM 中处理多表关系和多表操作是非常直观的,它支持多种关系类型,如一对一、一对多、多对多等
定义多表关系
一对一关系 (One-to-One)
假设我们有一个 User
模型和一个 Profile
模型,每个用户只有一个个人资料。
type User struct {gorm.ModelName stringProfile Profile
}type Profile struct {gorm.ModelUserID uintBio string
}
这里 Profile
表通过 UserID
字段与 User
表关联。
一对多关系 (One-to-Many)
假设我们有一个 User
模型和一个 Order
模型,每个用户可以有多个订单。
type User struct {gorm.ModelName stringOrders []Order
}type Order struct {gorm.ModelUserID uintAmount float64
}
这里 Order
表通过 UserID
字段与 User
表关联。
多对多关系 (Many-to-Many)
假设我们有一个 User
模型和一个 Language
模型,每个用户可以掌握多种语言,每种语言也可以被多个用户掌握。
type User struct {gorm.ModelName stringLanguages []Language `gorm:"many2many:user_languages;"`
}type Language struct {gorm.ModelName string
}
这里使用了一个中间表 user_languages
来存储 User
和 Language
之间的关系。
多表操作
创建关联记录
对于一对一和一对多的关系,你可以直接创建并保存关联的模型:
// 创建用户及其个人资料
func createUserWithProfile(db *gorm.DB) {db.Create(&User{Name: "John Doe",Profile: Profile{Bio: "Hello, I'm John"},})
}// 创建用户及其订单
func createUserWithOrders(db *gorm.DB) {db.Create(&User{Name: "Jane Doe",Orders: []Order{{Amount: 100.0},{Amount: 200.0},},})
}
对于多对多的关系,你需要先创建两边的模型,然后再建立它们之间的关系:
// 创建用户及其语言
func createUserWithLanguages(db *gorm.DB) {user := User{Name: "Alice"}languages := []Language{{Name: "English"}, {Name: "Spanish"}}// 先创建用户和语言db.Create(&user)for i := range languages {db.Create(&languages[i])}// 建立关系db.Model(&user).Association("Languages").Append(languages)
}
查询关联数据
GORM 支持预加载(Preloading)来一次性加载关联的数据:
// 预加载用户的个人资料
func getUserWithProfile(db *gorm.DB, userID uint) {var user Userdb.Preload("Profile").First(&user, userID)log.Printf("User: %+v, Profile: %+v", user, user.Profile)
}// 预加载用户的订单
func getUserWithOrders(db *gorm.DB, userID uint) {var user Userdb.Preload("Orders").First(&user, userID)log.Printf("User: %+v, Orders: %+v", user, user.Orders)
}// 预加载用户的语言
func getUserWithLanguages(db *gorm.DB, userID uint) {var user Userdb.Preload("Languages").First(&user, userID)log.Printf("User: %+v, Languages: %+v", user, user.Languages)
}
更新关联数据
更新关联数据通常涉及添加或移除关联项:
// 添加新的语言给用户
func addUserLanguage(db *gorm.DB, userID uint, languageID uint) {var user Userdb.First(&user, userID)db.Model(&user).Association("Languages").Append(&Language{Model: gorm.Model{ID: languageID}})
}// 从用户中移除一种语言
func removeUserLanguage(db *gorm.DB, userID uint, languageID uint) {var user Userdb.First(&user, userID)db.Model(&user).Association("Languages").Delete(&Language{Model: gorm.Model{ID: languageID}})
}
删除关联数据
删除关联数据时,可以选择级联删除或只是删除关联本身:
// 删除用户及其所有订单
func deleteUserAndOrders(db *gorm.DB, userID uint) {db.Delete(&User{}, userID)
}// 只删除用户的某个订单
func deleteOrder(db *gorm.DB, orderID uint) {db.Delete(&Order{}, orderID)
}// 删除用户及其所有语言关联
func deleteUserAndLanguages(db *gorm.DB, userID uint) {db.Delete(&User{}, userID)
}
事务
在 GORM 中,事务用于确保数据库操作的原子性,这意味着要么所有操作都成功执行,要么在遇到错误时所有操作都不会对数据库产生影响。以下是如何在 GORM 中使用事务的示例:
开始事务
在 GORM 中,你可以使用 Begin
方法来开始一个新的事务。
tx := db.Begin() // 开始事务
在事务中执行操作
在事务中,你可以执行任何你通常会在 GORM 中执行的操作,例如创建、更新、删除等。
user := User{Name: "张三", Age: 30}
if err := tx.Create(&user).Error; err != nil {tx.Rollback() // 遇到错误时回滚事务return err
}
post := Post{Title: "事务测试", Content: "这是一个事务中的帖子", UserID: user.ID}
if err := tx.Create(&post).Error; err != nil {tx.Rollback() // 遇到错误时回滚事务return err
}
提交事务
如果所有操作都成功执行,你可以调用 Commit
方法来提交事务。
if err := tx.Commit().Error; err != nil {return err // 提交事务时出错
}
回滚事务
如果在事务中的任何操作失败,你应该调用 Rollback
方法来回滚所有更改。
if err := tx.Rollback().Error; err != nil {return err // 回滚事务时出错
}
使用 Transaction
方法
GORM 还提供了一个 Transaction
方法,它接受一个函数作为参数,这个函数将在事务的上下文中执行。如果函数返回错误,事务将自动回滚;如果没有错误,事务将自动提交。
err := db.Transaction(func(tx *gorm.DB) error {user := User{Name: "李四", Age: 28}if err := tx.Create(&user).Error; err != nil {return err // 返回任何错误都会回滚事务}post := Post{Title: "另一个事务测试", Content: "这是另一个事务中的帖子", UserID: user.ID}if err := tx.Create(&post).Error; err != nil {return err // 返回任何错误都会回滚事务}return nil // 返回 nil 提交事务
})
if err != nil {// 事务回滚,处理错误
}
使用 Transaction
方法可以简化事务的处理,因为它自动处理了提交和回滚的逻辑。