Gorm:钩子Hook

Hook 是在创建、查询、更新、删除等操作之前、之后调用的函数。

支持以下的Hook方法

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
}

创建对象

注意 在 GORM 中保存、删除操作会默认运行在事务上, 因此在事务完成之前该事务中所作的更改是不可见的,如果您的钩子返回了任何错误,则修改将被回滚

可用Hook:

// 开始事务
BeforeSave
BeforeCreate
// 关联前的 save
// 插入记录至 db
// 关联后的 save
AfterCreate
AfterSave
// 提交或回滚事务
func (u *User) BeforeCreate(tx *gorm.DB) (err error) {
u.UUID = uuid.New()

if !u.IsValid() {
err = errors.New("can't save invalid data")
}
return
}

func (u *User) AfterCreate(tx *gorm.DB) (err error) {
if u.ID == 1 {
tx.Model(u).Update("role", "admin")
}
return
}

更新对象

可用Hook:

// 开始事务
BeforeSave
BeforeUpdate
// 关联前的 save
// 更新 db
// 关联后的 save
AfterUpdate
AfterSave
// 提交或回滚事务

删除对象

可用Hook:

// 开始事务
BeforeDelete
// 删除 db 中的数据
AfterDelete
// 提交或回滚事务

查询对象

// 从 db 中加载数据
// Preloading (eager loading)
AfterFind

修改当前操作

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

⚠️这里的afterCreate中db 当我们传入其他gorm操作执行SQL的时候,已经是HestiaInstanceModel

func RegisterHook(db *gorm.DB) {
// todo: 不能返回Error 可能出现部分失败
// Register callbacks
if err := db.Callback().Create().After("gorm:create").Register("hestia_instance_after_create", afterCreate); err != nil {
slog.Error("register hestia_instance_after_create failed", slog.Any("err", err))
}
}

func afterCreate(db *gorm.DB) {
if m, ok := db.Statement.Model.(*basismodel.HestiaInstanceModel); ok {
ctx := db.Statement.Context
username := getUsername(ctx)
db.Model(m).UpdateColumn("created_by", username)
db.Model(m).UpdateColumn("request_id", ctx.Value("request-id"))
repo := repository.NewDBInstanceRepository(middleware.CommonDB)
dbService := service.NewDBInstanceService(repo)
dbInstanceInfo, err := fromDBInStanceModel(m)
if err != nil {
slog.Error("afterCreate fromModel error", slog.Any("err", err))
return
}
if err := dbService.CreateDBInstance(context.Background(), dbInstanceInfo); err != nil {
slog.Error("afterCreate create db_instance error", slog.Any("err", err))
return
}
}
}