📦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

先来看看插入时候可以用的 hookhook生命周期

// 开始事务
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)