【GORM】Hook钩子函数,Session会话函数讲解及使用案例
GORM 中的 Hook 与 Session 详解
1. Hook
1.1 什么是 Hook?
Hook 是 GORM 提供的回调机制,允许开发者在模型操作的生命周期(如创建、更新、删除)中嵌入自定义逻辑。通过 Hook,可以在数据库操作前后执行特定的代码。
1.2 支持的生命周期回调
GORM 提供以下回调函数方法名,可以定义在模型结构体中:
BeforeSave
/AfterSave
BeforeCreate
/AfterCreate
BeforeUpdate
/AfterUpdate
BeforeDelete
/AfterDelete
AfterFind
hook函数要定义成绑定结构体的方法,传入*gorm.DB,返回error
1.3 Hook 示例
package main
import (
"fmt"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
)
// User 模型,包含 Hook
type User struct {
ID uint
Name string
Email string
}
// BeforeCreate 在创建之前执行逻辑
func (u *User) BeforeCreate(tx *gorm.DB) (err error) {
fmt.Println("BeforeCreate: 验证用户数据")
if u.Name == "" {
return fmt.Errorf("用户名不能为空")
}
return nil
}
// AfterCreate 在创建之后执行逻辑
func (u *User) AfterCreate(tx *gorm.DB) (err error) {
fmt.Println("AfterCreate: 创建完成后,发送欢迎邮件")
// 这里可以实现异步发送邮件的逻辑
return nil
}
func main() {
// 初始化数据库
db, _ := gorm.Open(sqlite.Open("test.db"), &gorm.Config{})
db.AutoMigrate(&User{})
// 创建用户
user := User{Name: "John Doe", Email: "john@example.com"}
if err := db.Create(&user).Error; err != nil {
fmt.Println("创建失败:", err)
} else {
fmt.Println("创建成功")
}
}
- hook函数会在对应行为开始前后自动调用
package _case
import (
"fmt"
"gorm.io/gorm"
)
// BeforeSave 事务开始前
func (t *Teacher) BeforeSave(tx *gorm.DB) error {
fmt.Println("hook BeforeSave")
return nil
}
func (t *Teacher) AfterSave(tx *gorm.DB) error {
fmt.Println("hook AfterSave")
return nil
}
func (t *Teacher) BeforeCreate(tx *gorm.DB) error {
fmt.Println("hook BeforeCreate")
return nil
}
func (t *Teacher) AfterCreate(tx *gorm.DB) error {
fmt.Println("hook AfterCreate")
return nil
}
func (t *Teacher) BeforeUpdate(tx *gorm.DB) error {
fmt.Println("hook BeforeUpdate")
return nil
}
func (t *Teacher) AfterUpdate(tx *gorm.DB) error {
fmt.Println("hook AfterUpdatee")
return nil
}
func (t *Teacher) BeforeDelete(tx *gorm.DB) error {
fmt.Println("hook BeforeDelete")
return nil
}
func (t *Teacher) AfterDelete(tx *gorm.DB) error {
fmt.Println("hook AfterDelete")
return nil
}
func (t *Teacher) AfterFind() error {
fmt.Println("hook AfterFind")
return nil
}
2. Session
2.1 什么是 Session?
Session 是 GORM 提供的一个上下文机制,用于管理数据库会话的状态,包括:
- SQL 语句生成的选项。
- 链式操作的隔离。
- 自定义事务级别。
2.2 Session 功能
以下是常见的 Session 使用场景:
- 隔离链式调用:
- 每次调用
Session
,会创建一个独立的上下文,不会影响其他调用。
- 每次调用
- 指定上下文:
- 可以传入自定义的
context.Context
。
- 可以传入自定义的
- 控制事务隔离级别:
- 通过
Session
设置事务的隔离级别和是否启用事务。
- 通过
- 禁用或启用功能:
- 例如,跳过 Hook、忽略默认值、指定表名等。
2.3 Session 示例
package main
import (
"context"
"fmt"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
)
func main() {
// 初始化数据库
db, _ := gorm.Open(sqlite.Open("test.db"), &gorm.Config{})
// 1. 隔离链式调用
fmt.Println("使用独立的 Session")
sessionDB := db.Session(&gorm.Session{})
sessionDB.Create(&User{Name: "Alice", Email: "alice@example.com"})
// 2. 自定义 Context
fmt.Println("传入自定义 Context")
ctx := context.Background()
db.WithContext(ctx).Create(&User{Name: "Bob", Email: "bob@example.com"})
// 3. 跳过 Hook
fmt.Println("跳过 Hook")
db.Session(&gorm.Session{SkipHooks: true}).Create(&User{Name: "", Email: "hook@example.com"})
// 4. 指定事务隔离级别
fmt.Println("指定事务隔离级别")
tx := db.Session(&gorm.Session{
PrepareStmt: true, // 启用缓存
NewDB: true, // 开启新的 DB 实例
}).Begin() // 开启事务
defer tx.Rollback()
tx.Create(&User{Name: "Charlie", Email: "charlie@example.com"})
tx.Commit()
}
3. Hook 与 Session 的组合
在实际开发中,Hook 和 Session 常结合使用。例如,在订单系统中,可以通过 Hook 验证订单状态,同时通过 Session 确保操作隔离。
案例:订单创建流程
package main
import (
"errors"
"fmt"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
)
type Order struct {
ID uint
UserID uint
ProductID uint
Quantity int
Status string
}
// BeforeSave 验证订单数据
func (o *Order) BeforeSave(tx *gorm.DB) (err error) {
if o.Quantity <= 0 {
return errors.New("订单数量必须大于0")
}
return nil
}
func main() {
// 初始化数据库
db, _ := gorm.Open(sqlite.Open("test.db"), &gorm.Config{})
db.AutoMigrate(&Order{})
// 使用 Session 创建订单
sessionDB := db.Session(&gorm.Session{SkipHooks: false})
order := Order{UserID: 1, ProductID: 2, Quantity: 5, Status: "Pending"}
if err := sessionDB.Create(&order).Error; err != nil {
fmt.Println("订单创建失败:", err)
} else {
fmt.Println("订单创建成功")
}
}
总结
Hook 使用要点:
- 用于在模型操作前后插入自定义逻辑。
- 避免复杂逻辑嵌套在 Hook 中,保持简洁。
Session 使用要点:
- 隔离链式调用,避免上下文污染。
- 灵活控制事务、上下文和功能开关。
- 配合 Hook,可在定制操作的同时保留数据库上下文的灵活性。
https://github.com/0voice