利用 callback 实现自动 etag 字段
为了实现乐观锁,我们经常会增加一个 etag 字段作为判断语句执行时的判断条件,类似如下的 model 定义:
对于这个 model,我们希望能在执行UPDATE/DELETE 语句的时候,自动增加一个判断条件 etag='some-uuid'
,这样我们在业务代码中就不用每次都加上 etag 字段的处理了,即方便,也避免了出错。
在 GORM 中,可以使用可以使用 hook 来实现这个需求,可以通过实现 BeforeCreate
/BeforeUpdate
/BeforeDelete
等方法来实现 etag 字段的自动处理。但是 hook 的方式有个问题,就是每个 model 都要实现一次这个方法。
一个简单的改进是,可以自己实现一个用于嵌入的 BaseModel
,类似于 gorm.Model
,在这个 model 上实现一次这些方法即可,其他 embed 了这个 BaseModel
的 model 就自动实现了这些方法。但是这个方法也有个问题,如果一个 model 想实现自己的 hook,那么就需要在自己的 hook 中调用 BaseModel
的对应 hook。但是这么做的话,不仅会降低效率,也会增加出错的概率。
GORM 还有另外一个方式来实现这个需求,就是 callback 系统。通过 callback,不仅可以实现在有 etag 字段存在时就自动处理的逻辑,也不会和 hook 系统冲突。下面是一个 create 和 update 时自动处理的 etag 的实现。
上述这段代码的总来说就是在 GORM 默认的 callbak 之前插入我们自定义的 callback,这些自定义的 callback 会修改将要执行的语句 (db.Statement
)。关于 GORM callback 的文档,见这里: Write Plugin。特别提一下,这个文档里说,默认的 callback 在文件 callbacks/callback.go 中的 RegisterDefaultCallbacks
函数中被注册,不过你在 gorm.io/gorm 是找不到这个函数被调用的地方的,因为这个函数是在 driver 中被调用的,可以查看 gorm.io/driver/postgres
的 Dialector
的实现。