1 回答
TA贡献1796条经验 获得超4个赞
您看到的 Insert 语句实际上是 Upsert 语句,您会注意到在插入部分之后,它们具有 ON CONFLICT 子句。如果主键已经在表中(即存在键冲突,因此得名),则这样做就是更新记录。按预期工作也是如此。Updates
但!
还有一个问题。就目前而言,这些语句仍会导致将新关系和新地址插入到数据库中,因为不会有主键冲突。这是因为您尚未为“关系”或“地址”行输入主键(您将看到插入中没有列)。id
经验法则:
Gorm 将主键为零的结构视为新行,并在保存时插入它们。推论:您只能更新具有非零主键的结构。
您可能认为您在调用中提供了主键,但这仅适用于顶级结构。当 gorm 处理关系时,它看不到任何主键,并假定您要添加新关系,而不是更新现有关系。Where
如果你考虑一下,这是有道理的:如果你不提供 Relation.ID,gorm怎么知道哪个关系目前与公司有关。知道这一点的方法是首先使用OwnerID和OwnerType进行选择以找出答案,但这不是gorm会为您做的事情(gorm通常试图做到极简主义,并且不会尝试自己找到比您提供的信息更多的信息)。
解决此问题的一种简单方法是让您的API用户在输入中提供 Relation.ID 和Address.ID,但在这种情况下,这是非常不方便的。
我发现解决此问题的一个很好的模式是首先加载目标根对象的当前状态以及要更新的相关关系,然后将来自API用户的更改应用于结构和关系,然后使用。这将保存所有字段,无论它们是否已更改。Save
另一种选择是将关系的现有 ID 放入您从 DB 中提取的版本,然后像您在此处所做的那样调用。update.Relation.ID
update.Relation.Addresses[i].ID
Updates
奖励:HasMany有更多的更新挑战
对于“地址”,需要特别注意,因为它是一个 HasMany 关系。当调用gorm永远不会删除关系时,例如,当您之前有3个地址并且现在只想拥有两个地址时,它只会更新其中两个地址,而第三个地址则悬而未决;这是为了防止错误地删除数据。相反,你必须明确你的意图。Save/Updates
然后,您要做的是使用关联模式来替换关联,但是在调用 :Updates
tx := db.Begin()
// do your thing
err := tx.
Session(&gorm.Session{FullSaveAssociations: true}).
Where("id = ?", id).
Omit("Relation.Addresses")
Updates(update).
Error
if err != nil {
tx.Rollback()
// handle error
}
// assuming update.Relation.ID is set
err = tx.
Model(&update.Relation).
Association("Addresses").
Replace(&update.Relation.Addresses)
if err != nil {
tx.Rollback()
// handle error
}
if err := tx.Commit().Error; err != nil {
tx.Rollback()
// handle error
}
- 1 回答
- 0 关注
- 120 浏览
添加回答
举报