钩子不执行需检查五点:接收者必须为指针、定义在模型包内、嵌套类型不继承、save跳过钩子、事务中仅before类返回error才回滚。

钩子不执行?先检查接收者类型和定义位置
GORM 只认 func (u *User) BeforeCreate(tx *gorm.DB) error 这种签名——必须是**指针接收者**、参数是 *gorm.DB、返回 error。值接收者(func (u User))或少参数/错返回值,都会静默失效。
- 钩子必须定义在模型 struct 所在的包内,不能写在另一个文件里再 import 类型扩展
- 嵌套类型(如
type Admin User)不会继承钩子,得单独为Admin实现 -
Save()方法默认跳过所有钩子,想触发时间戳更新,得用Update()或Updates()
BeforeUpdate 里改 UpdatedAt 却被覆盖?用 SetColumn 而非直接赋值
直接写 u.UpdatedAt = time.Now() 会让 GORM 把整个 struct 当作待更新字段,可能冲掉你原本只想改一个字段的意图(比如只调了 Update("status", "done"))。
- 正确做法是:
tx.Statement.SetColumn("updated_at", time.Now())——它只影响 SQL 生成,不污染当前 struct - 如果字段是
time.Time(非指针),钩子里赋值无效;必须用*time.Time或 GORM 内置别名如gorm.DeletedAt - 软删除时
Delete()不走AfterDelete,而是走BeforeDelete → AfterUpdate,所以要更新UpdatedAt得在BeforeDelete里做
事务中钩子出错会回滚吗?看是 Before 还是 After
只有 Before* 钩子返回非 nil error 才会中断 SQL 执行并回滚整个事务;After* 出错时事务早已提交,无法回滚,只能靠补偿逻辑兜底。
Go语言(Golang)1.26.0版本提供 Go 官方 Windows amd64 MSI 安装包下载入口,版本号 1.26.0,可用于旧项目维护、兼容性测试和指定版本开发环境配置。
-
BeforeCreate返回 error → 插入不执行,事务回滚 -
AfterUpdate返回 error → 更新已生效,日志或清理失败只能另想办法 - 在钩子里调用
tx.Create()等写操作,容易打断当前事务上下文,引发死锁或数据不一致
怎么传自定义参数进钩子?Set/Get 和 InstanceSet/InstanceGet 别混用
db.Set("key", val).Create(&u) 后,钩子里用 tx.Get("key") 能取到;但如果是关联创建(比如 user.CreditCards = []CreditCard{...}),GORM 会新建 statement,此时得用 InstanceSet + InstanceGet。
-
Set/Get是 session 级共享,适合全局上下文(如 traceID、租户 ID) -
InstanceSet/InstanceGet绑定到当前*Statement,关联操作中才有效 - 误把
InstanceGet用在主模型钩子里,会拿到nil;反过来在关联钩子里用Get,也可能因 statement 隔离而取不到
钩子看着简单,但签名、接收者、调用路径、事务边界、参数传递这五处,任意一个错位都会导致“写了却没跑”——调试时别急着怀疑 GORM,先盯住这五个点。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










