📦GORMv1.25.6
🏆GORM的Hook
⭐️ 在前面提到了在用户增删改查之前或者之后,可以使用Hook,来执行一系操作
⭐️ 如果您已经为模型定义了指定的方法,它会在创建、更新、查询、删除时自动被调用。如果任何回调返回错误,GORM
将停止后续的操作并回滚事务。
⭐️ 钩子方法的函数签名应该是 func(*gorm.DB) error
⭐️ 注意方法需要在实现在结构体,下面是钩子接口
type BeforeCreateInterface interface {
BeforeCreate(*gorm.DB) error
}
type AfterCreateInterface interface {
AfterCreate(*gorm.DB) error
}
type BeforeUpdateInterface interface {
BeforeUpdate(*gorm.DB) error
}
type AfterUpdateInterface interface {
AfterUpdate(*gorm.DB) error
}
type BeforeSaveInterface interface {
BeforeSave(*gorm.DB) error
}
type AfterSaveInterface interface {
AfterSave(*gorm.DB) error
}
type BeforeDeleteInterface interface {
BeforeDelete(*gorm.DB) error
}
type AfterDeleteInterface interface {
AfterDelete(*gorm.DB) error
}
type AfterFindInterface interface {
AfterFind(*gorm.DB) error
}
🌟 插入的Hook
先来看看插入时候可以用的 hook
和 hook
生命周期
// 开始事务
BeforeSave
BeforeCreate
// 关联前的 save
// 插入记录至 db
// 关联后的 save
AfterCreate
AfterSave
// 提交或回滚事务
测试一下
// User 创建结构体对应表
type User struct {
ID string `gorm:"primaryKey"`
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt gorm.DeletedAt `gorm:"index"`
Name string
Password string `gorm:"not null"`
}
func (This *User) BeforeCreate(tx *gorm.DB) (err error) {
//使用uuid初始化id
s := uuid.New().String()
s = strings.ReplaceAll(s, "-", "")
This.ID = s
fmt.Println()
if This.ID == "" {
return errors.New("添加UUID失败!")
}
fmt.Printf("添加成功UUID:%s\n", This.ID)
return nil
}
func (This *User) AfterCreate(tx *gorm.DB) (err error) {
find := tx.Where("id = ?", This.ID).Find(&This)
affected := find.RowsAffected
if affected == 0 {
return errors.New("添加失败!")
}
fmt.Printf("添加用户: %v 成功\n", This.Name)
return nil
}
然后编写代码添加用户
users := &[]model.User{
{Name: "张三", Password: "123456"},
}
tx := db.Model(&model.User{}).Create(users)
if tx.Error != nil {
println("添加失败", tx.Error.Error())
}
运行
添加成功UUID:d0e0a4053abf45b8ad140965d4e9db52
添加用户: 张三 成功
🌟 更新的Hook
更新时可用的 hook
// 开始事务
BeforeSave
BeforeUpdate
// 关联前的 save
// 更新 db
// 关联后的 save
AfterUpdate
AfterSave
// 提交或回滚事务
同样编写代码,这里我定义了一个新的结构体来保存更新前的数据
var (
olderU *OlderUser
)
type OlderUser struct {
ID string
Name string
Password string
}
func (This *User) BeforeUpdate(tx *gorm.DB) (err error) {
fmt.Println("正在更新数据....")
time.Sleep(2 * time.Second)
fmt.Printf("更新前数据id: %v 姓名: %v 密码: %v", This.ID, This.Name, This.Password)
This.saveBeforeUser(This.ID, This.Name, This.Password)
return nil
}
func (This *User) AfterUpdate(tx *gorm.DB) (err error) {
//对比是否更新
fmt.Println("正在对比数据....")
//更新后数据输出
fmt.Printf("更新后数据id: %v 姓名: %v 密码: %v", This.ID, This.Name, This.Password)
if This.ID == olderU.ID && This.Name == olderU.Name && This.Password == olderU.Password {
return errors.New("数据未更新!")
}
fmt.Println("更新数据成功!!")
return nil
}
//保存更新前数据
func (This *User) saveBeforeUser(id, name, password string) {
//保存
olderU = &OlderUser{
ID: id,
Name: name,
Password: password,
}
}
编写更新代码,执行
tx := db.Model(&model.User{}).Where("name = ?", "张三").Updates(map[string]interface{}{"name": "李四", "password": "000000"})
if tx.Error != nil {
println("更新失败", tx.Error.Error())
}
执行
正在更新数据....
更新前数据id: 姓名: 密码: 正在对比数据....
更新后数据id: 姓名: 李四 密码: 000000更新数据成功!!
2024/06/28 10:31:11 D:/learnCode/Go/GORM/GORM_Connect/main.go:26 SLOW SQL >= 200ms
[2036.113ms] [rows:3] UPDATE `GORMuser` SET `name`='李四',`password`='000000',`updated_at`='2024-06-28 10:31:
11.677' WHERE name = '张三' AND `GORMuser`.`deleted_at` IS NULL
🌟 查询的Hook
// 从 db 中加载数据
// Preloading (eager loading)
AfterFind
🌟 删除的Hook
// 开始事务
BeforeDelete
// 删除 db 中的数据
AfterDelete
// 提交或回滚事务
🌟 修改当前操作
func (u *User) BeforeCreate(tx *gorm.DB) error {
// 通过 tx.Statement 修改当前操作,例如:
tx.Statement.Select("Name", "Age")
tx.Statement.AddClause(clause.OnConflict{DoNothing: true})
// tx 是带有 `NewDB` 选项的新会话模式
// 基于 tx 的操作会在同一个事务中,但不会带上任何当前的条件
err := tx.First(&role, "name = ?", user.Role).Error
// SELECT * FROM roles WHERE name = "admin"
// ...
return err
}
🌟 跳过Hook
如果你想跳过 Hooks
方法,可以使用 SkipHooks
会话模式,例子如下
DB.Session(&gorm.Session{SkipHooks: true}).Create(&user)
DB.Session(&gorm.Session{SkipHooks: true}).Create(&users)
DB.Session(&gorm.Session{SkipHooks: true}).CreateInBatches(users, 100)