对象生命周期

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

如果您已经为模型定义了指定的方法,它会在创建、更新、查询、删除时自动被调用。如果任何回调返回错误,GORM 将停止后续的操作并回滚事务。

钩子方法的函数签名应该是 func(*gorm.DB) error

钩子

创建对象

创建时可用的钩子

  1. // 开始事务
  2. BeforeSave
  3. BeforeCreate
  4. // 关联前的 save
  5. // 插入记录至 db
  6. // 关联后的 save
  7. AfterCreate
  8. AfterSave
  9. // 提交或回滚事务

代码示例:

  1. func (u *User) BeforeCreate(tx *gorm.DB) (err error) {
  2. u.UUID = uuid.New()
  3. if !u.IsValid() {
  4. err = errors.New("can't save invalid data")
  5. }
  6. return
  7. }
  8. func (u *User) AfterCreate(tx *gorm.DB) (err error) {
  9. if u.ID == 1 {
  10. tx.Model(u).Update("role", "admin")
  11. }
  12. return
  13. }

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

  1. func (u *User) AfterCreate(tx *gorm.DB) (err error) {
  2. if !u.IsValid() {
  3. return errors.New("rollback invalid user")
  4. }
  5. return nil
  6. }

更新对象

更新时可用的钩子

  1. // 开始事务
  2. BeforeSave
  3. BeforeUpdate
  4. // 关联前的 save
  5. // 更新 db
  6. // 关联后的 save
  7. AfterUpdate
  8. AfterSave
  9. // 提交或回滚事务

代码示例:

  1. func (u *User) BeforeUpdate(tx *gorm.DB) (err error) {
  2. if u.readonly() {
  3. err = errors.New("read only user")
  4. }
  5. return
  6. }
  7. // 在同一个事务中更新数据
  8. func (u *User) AfterUpdate(tx *gorm.DB) (err error) {
  9. if u.Confirmed {
  10. tx.Model(&Address{}).Where("user_id = ?", u.ID).Update("verfied", true)
  11. }
  12. return
  13. }

删除对象

更新时可用的钩子

  1. // 开始事务
  2. BeforeDelete
  3. // 删除 db 中的数据
  4. AfterDelete
  5. // 提交或回滚事务

代码示例:

  1. // 在同一个事务中更新数据
  2. func (u *User) AfterDelete(tx *gorm.DB) (err error) {
  3. if u.Confirmed {
  4. tx.Model(&Address{}).Where("user_id = ?", u.ID).Update("invalid", false)
  5. }
  6. return
  7. }

查询对象

更新时可用的钩子

  1. // 从 db 中加载数据
  2. // Preloading (eager loading)
  3. AfterFind

代码示例:

  1. func (u *User) AfterFind(tx *gorm.DB) (err error) {
  2. if u.MemberShip == "" {
  3. u.MemberShip = "user"
  4. }
  5. return
  6. }

修改当前操作

  1. func (u *User) BeforeCreate(tx *gorm.DB) error {
  2. // 通过 tx.Statement 修改当前操作,例如:
  3. tx.Statement.Select("Name", "Age")
  4. tx.Statement.AddClause(clause.OnConflict{DoNothing: true})
  5. // 在没有 `WithConditions` 参数的清空下,tx 是一个新建会话模式
  6. // 基于 tx 的操作会在同一个事务中,但不会带上任何当前的条件
  7. var role Role
  8. err := tx.First(&role, "name = ?", user.Role).Error
  9. // SELECT * FROM roles WHERE name = "admin"
  10. // ...
  11. return err
  12. }