大咖好呀,我是恋喵大鲤鱼。
GORM(Golang Object Relational Mapping)是一个用于 Golang 的对象关系映射(ORM)库。
当需要插入或更新记录时,一般使用 Save 方法。如果只是插入,也可以使用 Create 方法。在使用 Save 方法多次更新同一条记录到 MySQL 时,却遇到了一个奇怪的问题。
先看一下 Save 方法的描述:
// Save updates value in database. If value doesn't contain a matching primary key, value is inserted.
func (db *DB) Save(value interface{}) (tx *DB)
Save 有两个作用,创建或更新记录。如果待保存的值不包含主键,则执行 Create,否则执行 Update(包含所有字段)。
如果是执行 Update 的话,模型字段即使是零值也会更新。这一点与 Updates 方法不同,Updates 默认只会更新非零值。
在使用 GORM v1.24.6 时,在并发调用 Save 方法更新同一个记录会报如下错误:
Error 1062 (23000): Duplicate entry 'xxx' for key 'PRIMARY'
奇怪的是,串行调用则不会报错。
无独有偶,我发现在 GORM Github 仓库已经有人提了类似的 Issues。
Duplicate primary key error returned when saving unmodified object #6171
我在该 Issue 中也补充了我遇到的问题。
另外,我还测试了一下上一个版本 v1.24.5 没有这个问题,说明该 Bug 是版本 v1.24.6 引入的新 Bug,非历史遗留的 Bug。
GORM 社区非常活跃,在我补充问题的当天便有人进行了回复。
从回复中可以看到,在 Issue #6171 之前,已经有人提了 PR #6149 来解决这个问题,只是还未被合入。
顺着回复的内容,找到 commit f387433,看了下提交内容。
从 commit message “Fix Save with stress tests” 和变更内容,推测 jinzhu 大佬是为了优化 Save 的更新性能,将插入前判断记录是否存在的条件去掉了。多次调用 Save 更新同一条记录时,发现记录没有被更新,则认为是新记录,便进行插入,然后就出现了主键冲突的错误。
在 Mar 23 当天,jinzhu 大佬可能已经意识到了问题的存在,便将 PR #6149 合入到主干,修复了这个问题。
如果大家遇到了同样的问题,请跳过 v1.24.6,使用之前或之后的版本,比如前一个版本 v1.24.5 或后一个版本 v1.25.0。
如果您喜欢这篇文章,欢迎关注微信公众号“恋喵大鲤鱼”了解最新精彩内容。
GORM Duplicate primary key error returned when saving unmodified object #6171