Hook
Hook是在CRUD操作之前之后调用的函数,使用时只需要在对应模型上定义hook方法即可,常见的hook方法:
| 操作 |
钩子方法 |
| 创建前/后 |
BeforeCreate, AfterCreate |
| 更新前/后 |
BeforeUpdate, AfterUpdate |
| 删除前/后 |
BeforeDelete, AfterDelete |
| 查询后 |
AfterFind |
| 保存前/后(含创建和更新) |
BeforeSave, AfterSave |
创建操作
1 2 3 4 5 6 7 8 9
| // 开始事务 BeforeSave BeforeCreate // 关联前的 save // 插入记录至 db // 关联后的 save AfterCreate AfterSave // 提交或回滚事务
|
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| 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 }
|
注意:GORM中保存和删除操作默认运行在事务上,在事务完成之前事务中所作更改是不可见的,若hook发生错误,修改将回滚
更新操作
1 2 3 4 5 6 7 8 9
| // 开始事务 BeforeSave BeforeUpdate // 关联前的 save // 更新 db // 关联后的 save AfterUpdate AfterSave // 提交或回滚事务
|
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| func (u *User) BeforeUpdate(tx *gorm.DB) (err error) { if u.readonly() { err = errors.New("read only user") } return }
func (u *User) AfterUpdate(tx *gorm.DB) (err error) { if u.Confirmed { tx.Model(&Address{}).Where("user_id = ?", u.ID).Update("verfied", true) } return }
|
删除操作
1 2 3 4 5
| // 开始事务 BeforeDelete // 删除 db 中的数据 AfterDelete // 提交或回滚事务
|
示例:
1 2 3 4 5 6 7
| func (u *User) AfterDelete(tx *gorm.DB) (err error) { if u.Confirmed { tx.Model(&Address{}).Where("user_id = ?", u.ID).Update("invalid", false) } return }
|
查询操作
1 2 3
| // 从 db 中加载数据 // Preloading (eager loading) AfterFind
|
示例:
1 2 3 4 5 6
| func (u *User) AfterFind(tx *gorm.DB) (err error) { if u.MemberShip == "" { u.MemberShip = "user" } return }
|
修改当前操作
通过tx.Statement修改接下来执行的操作
1 2 3 4 5
| func (u *User) BeforeCreate(tx *gorm.DB) error { tx.Statement.Select("Name", "Age") tx.Statement.AddClause(clause.OnConflict{DoNothing: true}) }
|
这里限定了接下来的操作只对name和age两个字段起作用,且若发生冲突则跳过
事务中查询其他表
1 2 3 4 5 6 7 8
| func (u *User) BeforeCreate(tx *gorm.DB) error { err := tx.First(&role, "name = ?", user.Role).Error return err }
|
tx是hook传入的会话,和即将执行的insert共享一个事务,tx.First会在该事务中执行SELECT查询roles表,获取角色信息
可以基于查询结果继续执行逻辑
事务
默认事务
GORM会在事务里执行写入操作(创建更新删除),若没有要求,可以在初始化时禁用它,可以获得30%+性能提升
1 2 3 4 5 6 7 8 9 10
| db, err := gorm.Open(sqlite.Open("gorm.db"), &gorm.Config{ SkipDefaultTransaction: true, })
tx := db.Session(&Session{SkipDefaultTransaction: true}) tx.First(&user, 1) tx.Find(&users) tx.Model(&user).Update("Age", 18)
|
语法
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| db.Transaction(func(tx *gorm.DB) error { if err := tx.Create(&Animal{Name: "Giraffe"}).Error; err != nil { return err }
if err := tx.Create(&Animal{Name: "Lion"}).Error; err != nil { return err }
return nil })
|
嵌套事务
支持嵌套事务,可以回滚大事务中的一部分操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| db.Transaction(func(tx *gorm.DB) error { tx.Create(&user1) tx.Transaction(func(tx2 *gorm.DB) error { tx2.Create(&user2) return errors.New("rollback user2") }) tx.Transaction(func(tx3 *gorm.DB) error { tx3.Create(&user3) return nil })
return nil })
|
手动事务
可以手动控制事务开启关闭的方法
db.Begin():开始事务,这之后调用的函数需要以事务名调用
tx.Create():创建db操作
tx.Rollback():遇到错误时回滚错误
tx.Commit():提交事务
保存点
可以通过 SavePoint、Rollbackto
方法创建保存点和回滚至保存点
1 2 3 4 5 6 7 8
| tx := db.Begin() tx.Create(&user1)
tx.SavePoint("sp1") tx.Create(&user2) tx.RollbackTo("sp1")
tx.Commit()
|